Warning
For local development and testing only. Not for production or any instance holding real data.
- Uses
dhis2/core-dev:latest, an unstable development snapshot (2.44-SNAPSHOT) that changes often, may be broken or have breaking schema changes, and is not a supported release. Upgrades between builds are not guaranteed. - The default DHIS2 login is insecure (
admin/district). Change it if the stack is reachable by anyone else. The database password is randomly generated on first start (see Database password). - No TLS/HTTPS, no reverse proxy hardening, no firewalling: DHIS2 (port 8080) is published on all host interfaces.
- The Glowroot UI (port 4000) has no authentication and exposes request and SQL data; it is bound to
127.0.0.1by default. - Single node, no backups, no high availability, no tuning for load. The database may run under emulation on Apple Silicon.
- Do not load dumps containing real or personal health data into this stack.
- Unofficial community project, not affiliated with or endorsed by the DHIS2 project or HISP. Provided "as is", without warranty.
For production, follow the DHIS2 installation documentation and use a released, supported version.
Docker Compose stack for running the development version of DHIS2 (currently 2.44-SNAPSHOT,
image dhis2/core-dev) locally. It provides a
multi-component setup (DHIS2 + PostgreSQL + Redis, configured through a dhis.conf), and adds
Glowroot APM and an optional database dump loader.
Only official images are used, and no local folder is required (everything lives in named volumes).
- Architecture
- Requirements
- Quick start
- Configuration (environment variables)
- Loading a database dump
- Glowroot
- Volumes and data persistence
- Common operations
- Troubleshooting
| Service | Image | Purpose | Host port |
|---|---|---|---|
pass_init |
busybox |
One-shot: generates the random DB password and writes dhis.conf |
- |
glowroot_init |
busybox |
One-shot: downloads Glowroot into a volume, fixes volume permissions | - |
db_dump |
busybox |
One-shot: provides an optional DB dump to PostgreSQL | - |
database |
postgis/postgis (official) |
PostgreSQL + PostGIS, DB dhis2, user dhis |
- |
redis |
redis |
Redis cache and cache invalidation | - |
dhis2 |
dhis2/core-dev |
DHIS2 application (Tomcat, Java 17) with the Glowroot agent | 8080, 4000 (localhost) |
Startup order: pass_init, glowroot_init and db_dump → database (healthy) and redis (healthy) → dhis2.
The dhis.conf is generated by pass_init into the dhis2_home volume (/opt/dhis2/dhis.conf) on every start:
connection.driver_class = org.postgresql.Driver
connection.url = jdbc:postgresql://database/dhis2
connection.username = dhis
connection.password = <generated or DB_PASSWORD>
redis.enabled = on
redis.cache.invalidation.enabled = on
redis.host = redis
redis.port = 6379
leader.time.to.live.minutes = 4
server.base.url = <SERVER_BASE_URL>
No password is hard-coded. On the first start pass_init generates a random 32-character password,
stores it in the db_password volume, and uses it for both PostgreSQL (POSTGRES_PASSWORD_FILE) and
dhis.conf, so they always match. To read it (e.g. to connect with a SQL client):
docker compose exec database cat /secrets/db_passwordTo choose your own password instead, set DB_PASSWORD before the first start. Once the database
exists the password is fixed (changing the variable later has no effect; recreate the volumes with
docker compose down -v).
- Docker Engine / Docker Desktop with Compose v2 (
docker compose). - ~4 GB of free RAM for the containers (DHIS2 + PostgreSQL + Glowroot).
- Internet access on first start (image pulls, Glowroot download, optional dump download).
- Apple Silicon (arm64): the official
postgis/postgisimage is published forlinux/amd64only, so the compose file setsplatform: linux/amd64for the database (runs under emulation). On an amd64 host, or if you use a multi-arch PostGIS tag, setDB_PLATFORMaccordingly (see below).
docker compose up -d
docker compose logs -f dhis2The first start of the dev image takes a few minutes (database schema creation). When ready:
- DHIS2: http://localhost:8080 — default credentials
admin/district(change them immediately on any shared instance). - Health check:
curl http://localhost:8080/api/ping→pong - Glowroot: http://localhost:4000
Stop (keeping data): docker compose down
Stop and delete all data: docker compose down -v
Set them in the shell or in a .env file next to docker-compose.yml.
| Variable | Default | Description |
|---|---|---|
DHIS2_TAG |
latest |
dhis2/core-dev tag. latest is the moving development build (2.44). To pin a build, append its digest (see Pinning). |
POSTGIS_TAG |
16-3.5 |
postgis/postgis tag (PostgreSQL major version - PostGIS version). |
DB_PLATFORM |
linux/amd64 |
Platform for the database container. Use linux/arm64 only if your PostGIS tag supports it. |
REDIS_TAG |
7 |
redis tag. |
DB_PASSWORD |
(empty = random) | Password of the dhis PostgreSQL user. If empty, a random one is generated on first start (see Database password). Only applies at first start. |
SERVER_BASE_URL |
http://localhost:8080 |
server.base.url in dhis.conf. |
GLOWROOT_VERSION |
0.14.2 |
Glowroot release downloaded from GitHub on first start. |
DHIS2_DB_DUMP_URL |
(empty) | URL of a .sql.gz dump to load on first start. See below. |
Example .env:
# Pinned dev build (see "Pinning the DHIS2 build"); use plain `latest` to follow the newest build
DHIS2_TAG=latest@sha256:40b28c9f9155ce4509579083f1efabb6b9b24c5635676d89243e4c836c9997cf
DB_PASSWORD=a-strong-password # optional; omit for a random password
SERVER_BASE_URL=http://localhost:8080
DHIS2_DB_DUMP_URL=https://databases.dhis2.org/sierra-leone/2.42.0/dhis2-db-sierra-leone.sql.gzdhis2/core-dev has no version-numbered tags for 2.44 (only latest/master, which change on every
build), so pin by image digest for reproducible setups:
docker buildx imagetools inspect dhis2/core-dev:latest | grep Digest
# then in .env:
DHIS2_TAG=latest@sha256:<digest>The copy-ready .env.example lists all variables.
The db_dump service puts dump files in a volume mounted at /docker-entrypoint-initdb.d of the
database container. The official PostgreSQL image executes *.sql, *.sql.gz (and *.sh) files
there only when the data directory is empty, i.e. on the very first start. The dump is loaded into
database dhis2 as user dhis.
DHIS2_DB_DUMP_URL=https://databases.dhis2.org/sierra-leone/2.42.0/dhis2-db-sierra-leone.sql.gz \
docker compose up -dThe file is cached in the db_dump volume as dump.sql.gz (downloaded once).
Create a db-dump/ folder next to the compose file and drop your plain-SQL dump (.sql or
.sql.gz) in it:
mkdir -p db-dump
cp ~/backups/dhis2.sql.gz db-dump/
docker compose up -dThe folder is optional: if it does not exist Docker creates an empty one, and nothing is loaded.
-
The dump must be plain SQL (
pg_dump -Fp), not custom/directory format (-Fc/-Fd). For custom-format dumps, restore manually (below). -
Recommended way to create a compatible dump:
pg_dump --no-owner --no-privileges -Fp dhis2 | gzip > dhis2.sql.gz. If objects are owned by another role, loading will show errors; use--no-owner. -
Dumps must be from a DHIS2 version not newer than the one you run. DHIS2 applies migrations at startup, so an older dump (e.g. 2.42) is upgraded automatically by 2.44-SNAPSHOT. Large dumps can make that first start take a long time.
-
Loading happens automatically only once. To load a (different) dump into an existing setup, recreate the database volume (destroys DB data):
docker compose down docker volume rm "$(docker compose config --format json | jq -r .name)_pg_data" docker volume rm "$(docker compose config --format json | jq -r .name)_db_dump" # only if the dump changed docker compose up -d
(Or simply
docker compose down -vto wipe everything.) -
Alternatively restore manually into a running database:
# plain SQL gunzip -c dump.sql.gz | docker compose exec -T database psql -U dhis -d dhis2 # custom format docker compose exec -T database pg_restore -U dhis -d dhis2 --no-owner < dump.dump docker compose restart dhis2
Glowroot is a Java APM (transactions, slow queries, JVM metrics).
glowroot_initdownloads the official distribution (GLOWROOT_VERSION) from the Glowroot GitHub releases into theglowrootvolume — no localglowroot/folder needed.dhis2loads it viaJAVA_OPTS=-javaagent:/opt/glowroot/glowroot.jarand exposes the UI on http://localhost:4000. The init step configures it to listen on0.0.0.0so it is reachable through the published port.- Security: Glowroot's UI is unauthenticated by default, so the compose file publishes it on
127.0.0.1only. Do not expose it on other interfaces without configuring authentication. - Glowroot data and configuration persist in the
glowrootvolume. - To disable it: remove the
JAVA_OPTSline, the4000port and theglowrootvolume mount from thedhis2service. - Extra JVM options (heap, remote debugging) can be appended to
JAVA_OPTS, e.g.-Xmx2g -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8081(also publish8081).
| Volume | Content | Mounted in |
|---|---|---|
pg_data |
PostgreSQL data | database:/var/lib/postgresql/data |
redis_data |
Redis persistence | redis:/data |
dhis2_home |
Generated dhis.conf and DHIS2 files/ (file resources, apps) |
dhis2:/opt/dhis2 |
db_password |
Generated database password | database:/secrets (read-only) |
glowroot |
Glowroot distribution, config and collected data | dhis2:/opt/glowroot |
db_dump |
Dump file(s) loaded on first DB start | database:/docker-entrypoint-initdb.d |
Volume names are prefixed with the Compose project name (the folder name by default).
docker compose down keeps volumes; docker compose down -v removes them.
docker compose ps # status
docker compose logs -f dhis2 # DHIS2 / Tomcat logs
docker compose pull && docker compose up -d # update to the newest dev build
docker compose restart dhis2
docker compose exec database psql -U dhis dhis2 # SQL shell
docker compose exec redis redis-cli # Redis shell
docker compose exec -T database pg_dump -U dhis --no-owner dhis2 | gzip > backup.sql.gz # backupcore-dev:latest changes frequently and a newer build may require a newer schema; if you pull a new
image and startup fails, check the logs or recreate the database.
DHIS2 can run several nodes sharing Redis and the files storage. In Compose, remove the fixed 8080/4000
port mappings from dhis2 (or map ranges) and run docker compose up -d --scale dhis2=2, putting a
reverse proxy in front. Glowroot is per-JVM and its volume is shared, so keep it to one node unless
you give each its own volume.
| Symptom | Cause / fix |
|---|---|
password authentication failed for user "dhis" |
pg_data was initialised with a different password (or by another stack). The password is generated or set only at first start. Use docker compose down -v to start over. |
AccessDeniedException: /opt/dhis2/files/apps |
dhis2_home not writable by the DHIS2 user. pass_init fixes permissions; make sure it ran (docker compose ps -a). |
no matching manifest for linux/arm64 |
Official PostGIS has no arm64 build for that tag; keep DB_PLATFORM=linux/amd64 (default). |
| Database is slow on Apple Silicon | Emulated amd64 PostgreSQL. Use a native arm64 PostGIS image if performance matters. |
| Dump not loaded | Data directory already existed; see dump notes. Check docker compose logs database and docker compose logs db_dump. |
| Port 8080 or 4000 in use | Change the host side of the port mapping, e.g. "8081:8080", and update SERVER_BASE_URL. |
| DHIS2 container "Up" but site is down | Tomcat stays up even if the app failed to start. Check docker compose logs dhis2 | grep -E "Caused by|SEVERE". |
| Glowroot UI not reachable | Check docker compose logs glowroot_init (download failure) and that port 4000 is published. |
Built with ❤️ by MillacuraFA
MIT. DHIS2 is a trademark of its respective owners; this repository is not affiliated with them.