Step by Step Guide: Running DHIS2 and Its Database on Your Own Machine With Docker
Every other topic needs a DHIS2 to work in. This one gives you your own, running on your laptop, with two files and a handful of commands. It covers both situations: the machine that has never seen DHIS2 and has to download it, and the machine where the image is already there.
Two containers run side by side on your own machine. One is the database, the other is DHIS2 itself. They talk to each other over a private network, and your browser reaches DHIS2 on one port.
Docker is the only thing you install. DHIS2 and the database then run inside it, and nothing else is added to your machine.
| Your machine | Download from | What to install |
|---|---|---|
| Windows 10 or 11 | https://www.docker.com/products/docker-desktop/ | Docker Desktop for Windows, AMD64 build |
| Mac, Apple silicon | https://www.docker.com/products/docker-desktop/ | Docker Desktop for Mac, Apple chip |
| Mac, Intel | https://www.docker.com/products/docker-desktop/ | Docker Desktop for Mac, Intel chip |
| Ubuntu or Debian | https://docs.docker.com/engine/install/ubuntu/ | Docker Engine and the compose plugin |
wsl --install, restart the machine,
then run the Docker Desktop installer, keep the box for WSL2 ticked, and start Docker
Desktop from the Start menu. The whale icon in the system tray must say Running.
sudo usermod -aG docker $USER and log out and in again, so you do not need
sudo for every command.
Git is only used to copy the two setup files onto your machine.
| Your machine | Download from | Notes |
|---|---|---|
| Windows | https://git-scm.com/download/win | Accept every default. It installs Git Bash, which is a good terminal for these commands |
| Mac | https://git-scm.com/download/mac | Or run xcode-select --install, which also gives you git |
| Ubuntu or Debian | Terminal | sudo apt update && sudo apt install git |
Every command below talks to Docker. If Docker Desktop is not running, the terminal answers with an error no matter how correct your file is, so open Docker first and wait for it to finish starting, then open PowerShell or your terminal.
| Machine | How to start it | How you know it is ready |
|---|---|---|
| Windows | Start menu → Docker Desktop | The whale in the system tray stops animating and the app says Engine running |
| Mac | Applications → Docker | The whale in the menu bar is steady |
| Ubuntu | sudo systemctl start docker | docker info answers without an error |
error during connect: ... the docker daemon is not running. Nothing is broken.
Start Docker Desktop, wait, and run the command again.
You need Docker Desktop and git. On Windows, Docker Desktop needs WSL2 turned on.
| What | Why | How much |
|---|---|---|
| Docker Desktop | Runs both containers | Latest version |
| WSL2, on Windows | Docker needs it | Turned on in Docker settings |
| Memory | DHIS2 and the database together | 8 GB, with at least 4 GB given to Docker |
| Disk | Images and data | About 5 GB free |
| git | To fetch the two files | Any version |
git clone https://github.com/mohamedsillahkanu/dhis2-local-set-up.git
cd dhis2-local-set-upThis file is the whole setup. Nothing is installed on your machine except Docker.
services:
database:
image: postgis/postgis:13-3.3-alpine
container_name: dhis2_test_db
environment:
POSTGRES_USER: dhis
POSTGRES_PASSWORD: dhis
POSTGRES_DB: dhis2
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- dhis2-test-db-data:/var/lib/postgresql/data
ports:
- "5433:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dhis -d dhis2"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- dhis2-test-network
dhis2:
image: dhis2/core:2.42.1
container_name: dhis2_test
depends_on:
database:
condition: service_healthy
environment:
DHIS2_DATABASE_HOST: database
DHIS2_DATABASE_NAME: dhis2
DHIS2_DATABASE_USERNAME: dhis
DHIS2_DATABASE_PASSWORD: dhis
DHIS2_HOME: /DHIS2_home
ports:
- "8010:8080"
volumes:
- dhis2-test-data:/DHIS2_home
- ./dhis.conf:/opt/dhis2/dhis.conf
restart: unless-stopped
networks:
- dhis2-test-network
volumes:
dhis2-test-db-data:
name: dhis2-test-db-data
dhis2-test-data:
name: dhis2-test-data
networks:
dhis2-test-network:
name: dhis2-test-network
driver: bridge| Line | What it does |
|---|---|
services: | Everything under it is one container |
image: postgis/postgis:13-3.3-alpine | PostgreSQL 13 with PostGIS 3.3, the small Alpine build. DHIS2 needs PostGIS for maps |
container_name: dhis2_test_db | A fixed name, so commands and logs are readable |
POSTGRES_USER / PASSWORD / DB | Creates the database dhis2 and the user dhis on first start. These must match dhis.conf |
PGDATA | Where the database writes its files inside the container |
volumes: dhis2-test-db-data | Keeps those files outside the container, so data survives a restart |
ports: "5433:5432" | Left is your machine, right is inside. 5433 avoids clashing with a PostgreSQL you already run |
healthcheck | Runs pg_isready every 10 seconds until the database answers |
restart: unless-stopped | Comes back after a reboot, unless you stopped it yourself |
image: dhis2/core:2.42.1 | The DHIS2 version. Change this number to try another release |
depends_on: condition: service_healthy | DHIS2 waits for the healthcheck to pass. Without it, DHIS2 starts too early and dies |
DHIS2_DATABASE_HOST: database | The service name above, which is also its hostname on the network |
DHIS2_HOME: /DHIS2_home | Where DHIS2 keeps its configuration and files |
ports: "8010:8080" | DHIS2 listens on 8080 inside, you reach it on 8010. Change the left number if 8010 is busy |
./dhis.conf:/opt/dhis2/dhis.conf | Puts your file inside the container. The dot means the folder you cloned |
volumes: at the end | Declares the two named volumes where everything is kept |
networks: driver: bridge | A private network for the two containers only |
Five lines. They tell DHIS2 which database to use and how to sign in to it.
connection.dialect = org.hibernate.dialect.PostgreSQLDialect
connection.driver_class = org.postgresql.Driver
connection.url = jdbc:postgresql://database:5432/dhis2
connection.username = dhis
connection.password = dhis| Line | What it does |
|---|---|
connection.dialect | Tells Hibernate it is talking to PostgreSQL |
connection.driver_class | The JDBC driver DHIS2 loads |
connection.url | database is the service name, 5432 is the port inside the network, dhis2 is the database name |
connection.username | dhis, the same as POSTGRES_USER |
connection.password | dhis, the same as POSTGRES_PASSWORD |
localhost instead of database in the url, which fails because
localhost inside a container is the container itself, and using 5433, which is the port on
your machine rather than the port on the private network.
Everything from here depends on one question: is the DHIS2 image already on this machine? Ask Docker rather than guessing.
docker images | grep -E "dhis2|postgis"| What comes back | Go to |
|---|---|
| Nothing, or a different version | Scenario 1, the next step |
| Both lines, with 2.42.1 | Scenario 2, the step after that |
The whole sequence, run from inside the cloned folder. The pull is the slow part, about 1 GB, and it happens once on this machine.
| # | Command | What it does | How long |
|---|---|---|---|
| 0 | Open Docker Desktop and wait for Running | Nothing else works until the engine is up | Half a minute |
| 1 | cd dhis2-local-set-up | Moves into the folder with the two files | Instant |
| 2 | docker compose pull | Downloads the DHIS2 and database images | Five to fifteen minutes |
| 3 | docker compose up -d | Creates the network, the volumes and both containers | Under a minute |
| 4 | docker compose ps | Database healthy, DHIS2 started | Instant |
| 5 | docker compose logs -f dhis2 | Follows the first start, which builds every table | Three to ten minutes |
| 6 | Wait for Server startup in ... ms | The line that says DHIS2 is listening, then Ctrl + C | Then you are done waiting |
| 7 | Open http://localhost:8010 | Sign in as admin, district | Instant |
Nothing is downloaded. The same commands as Scenario 1 without the pull. If the volume already holds a database, the start takes seconds rather than minutes.
| # | Command | What it does | How long |
|---|---|---|---|
| 0 | Open Docker Desktop and wait for Running | Nothing else works until the engine is up | Half a minute |
| 1 | cd dhis2-local-set-up | Moves into the folder | Instant |
| 2 | docker images | grep dhis2 | Confirms the version matches the compose file | Instant |
| 3 | docker compose up -d | Starts both containers from the images you already have | Under a minute |
| 4 | docker compose ps | Database healthy, DHIS2 up | Instant |
| 5 | docker compose logs -f dhis2 | Minutes for a new database, seconds for one that already exists | Seconds to minutes |
| 6 | Open http://localhost:8010 | Sign in as admin, district | Instant |
docker compose pull once and follow Scenario 1.
docker save dhis2/core:2.42.1 -o dhis2.tar, copy
the file across, and on each other machine run docker load -i dhis2.tar.
Everyone is then in Scenario 2 and nobody waits for the download.
Four commands cover almost everything.
docker compose up -d # start
docker compose down # stop, keep the data
docker compose logs -f dhis2 # watch the log
docker compose restart dhis2 # restart DHIS2 only| What you see | Why | What to do |
|---|---|---|
| The docker daemon is not running | Docker Desktop is closed | Open it, wait for Running, run the command again |
| Ports are not available | 8010 or 5433 is in use | Change the left number in the ports line |
| DHIS2 keeps restarting | It cannot reach the database | Check the url line in dhis.conf says database:5432 |
| Exit code 137 | Docker ran out of memory | Give Docker at least 4 GB in its settings |
| Blank page on 8010 | Startup is not finished | Wait for the Server startup line in the log |
| dhis.conf not found | The command was run from another folder | cd into the cloned folder first |
| Password authentication failed | dhis.conf and the compose file disagree | Make both say dhis and dhis |
The instance is empty. Nothing in DHIS2 can be built until the organisation unit hierarchy exists, so go on to Topic 1 and create it, by hand or from the CSV file provided there.
| Order | Topic |
|---|---|
| 1 | Organisation units |
| 2 | Data elements |
| 3 | Category combinations |
| 4 | The data set, then the form and data entry |