Installation¶
The NidanEHR services deploy as a single Docker Compose project from the nidan-docker/ directory. The configuration is managed through a .env file, routing EMR, laboratory, ERP, PACS, integration middleware, and reporting services onto a single private network behind a reverse proxy.
Applies to
The current NidanEHR stack defined by nidan-docker/docker-compose.yml: OpenMRS 2.8.x ·
OpenELIS-Global 2 · Odoo 19 CE · Orthanc · Kafka 7.5 · Keycloak. Service images and versions are
pinned in that file.
Every value on this page is a dummy
The example values below - especially passwords, secrets, and admin credentials - are
placeholders for illustration. Generate your own secrets and replace every change-me-*
value before deploying. Never commit a real .env.
Prerequisites¶
- Docker Engine 24+ and the Docker Compose v2 plugin.
- ~8 GB RAM free for the full stack; more for production load.
- The monorepo checked out, so
nidan-docker/sits next to its sibling components. - For a build-from-source deploy: the custom OpenMRS OMODs built and copied into
nidan-docker/openmrs/custom_modules/(see Build the OpenMRS image).
Quick start¶
cd nidan-docker
# 1. Create your environment file from the template
cp env.template .env
# then edit .env - set domains and replace every secret (see tables below)
# 2. (build-from-source only) build custom OMODs into openmrs/custom_modules/ first
# then build the images that are built locally
docker-compose build openmrs-backend openmrs-frontend gateway superset
# 3. Start everything
docker-compose up -d
# 4. Watch a service come up
docker-compose logs -f openmrs-backend
When the stack is healthy, the gateway serves everything on one host:
| URL (through the gateway) | Application |
|---|---|
/ |
Dashboard landing page |
/openmrs, /openmrs/spa/ |
OpenMRS EMR (O3 SPA) |
/odoo |
Odoo ERP |
/openelis/ |
OpenELIS lab UI |
/api/OpenELIS-Global/ |
OpenELIS backend API |
/orthanc-container |
Orthanc PACS |
/auth |
Keycloak |
/superset |
Superset BI |
Published host ports¶
The gateway is the only surface you need in production. The other published ports exist for direct or debug access and can be closed off behind the gateway/firewall.
| Host port | Service | Purpose |
|---|---|---|
80, 443, 8443 |
gateway | HTTP, HTTPS, and the OpenELIS-only TLS port |
3307 |
openmrs-db | MariaDB (maps to container 3306) |
8069 |
odoo | Odoo direct access |
8042, 4242 |
orthanc | DICOMweb/HTTP and DICOM |
15432 |
openelis-db | OpenELIS PostgreSQL (container 5432) |
8083 |
openelis | OpenELIS backend (container 8080) |
8085 |
openelis-frontend | OpenELIS UI (container 80) |
8088 |
superset | Superset direct access (remove in production) |
Build the OpenMRS image¶
Custom OMODs are deliberately kept out of git. Build and copy them before building the backend image, in dependency order:
# from the repo root, for each module:
cd openmrs-backend/openmrs-module-<name>
mvn package -DskipTests
cp omod/target/*.omod ../../nidan-docker/openmrs/custom_modules/
Build order matters: fhir2 → medication-administration → ipd. Building the image without
copying the OMODs produces a working distro that is silently missing functionality.
Environment Configuration Reference¶
The tables below define variables from env.template grouped by application. Hostnames such as openmrs-db or odoo resolve internally on the nidan-network bridge and must not be altered unless the corresponding container names change.
Gateway & TLS¶
| Variable | Example value | Description |
|---|---|---|
GATEWAY_IMAGE |
trigonaltechnology/gateway:latest |
Nginx gateway image to pull/run. |
GATEWAY_DOMAIN |
ehr.example.org |
Public hostname the gateway serves. |
DASHBOARD_ODOO_URL |
https://ehr.example.org:8069 |
Odoo link shown on the dashboard card. |
CERTBOT_EMAIL |
ops@example.org |
Contact email for Let's Encrypt registration. |
CERTBOT_DOMAINS |
ehr.example.org |
Comma-separated domains to request certs for. |
SSL_TRUSTSTORE_PATH |
/certs/truststore.p12 |
Optional truststore for outbound HTTPS/FHIR clients. |
SSL_TRUSTSTORE_PASSWORD |
change-me-truststore |
Password for the truststore above. |
SSL_KEYSTORE_PATH |
/certs/keystore.p12 |
Optional keystore path. |
SSL_KEYSTORE_PASSWORD |
change-me-keystore |
Password for the keystore above. |
OpenMRS (EMR)¶
OpenMRS owns patients, encounters, and orders. The active Compose file runs its database on
MariaDB with row-based binlog enabled (Debezium/CIS reads it). The nidan-cis middleware
authenticates to OpenMRS as a dedicated sync account.
OpenMRS - database & app¶
| Variable | Example value | Description |
|---|---|---|
OPENMRS_DB_NAME |
openmrs |
OpenMRS schema/database name. |
OPENMRS_DB_USER |
openmrs |
Application DB user. |
OPENMRS_DB_PASSWORD |
change-me-omrs-db |
Password for the app DB user. |
OPENMRS_DB_ROOT_PASSWORD |
change-me-omrs-root |
MariaDB root password (also used by Debezium). |
OPENMRS_DB_HOST |
openmrs-db |
DB service hostname on the network. |
OPENMRS_DB_PORT |
3306 |
Container-internal DB port (host-published as 3307). |
POSTGRES_MAX_SLOT_WAL_KEEP_SIZE |
1GB |
WAL retention cap (only used if the Postgres profile is enabled). |
OMRS_DEBUG |
false |
Enable verbose OpenMRS logging. |
OpenMRS - REST / sync account¶
| Variable | Example value | Description |
|---|---|---|
OPENMRS_USERNAME |
nidan-sync |
Account CIS uses to read/write OpenMRS; keep equal to NIDAN_SYNC_USERNAME. |
OPENMRS_PASSWORD |
change-me-sync-pass |
Password for the sync account; keep equal to NIDAN_SYNC_PASSWORD. |
OPENMRS_ADMIN_USERNAME |
admin |
Real admin, used by CIS only at startup to create the sync account. |
OPENMRS_ADMIN_PASSWORD |
change-me-omrs-admin |
Password for the bootstrap admin. |
NIDAN_SYNC_USERNAME |
nidan-sync |
Shared sync service account the middleware provisions in OpenMRS/OpenELIS. |
NIDAN_SYNC_PASSWORD |
change-me-sync-pass |
Password for the shared sync account. Change before deploy. |
OPENMRS_IDENTIFIER_TYPE_UUID |
c0e1d2f3-…-1a2b3c4d5e6f |
Patient identifier type CIS assigns. |
OPENMRS_DEFAULT_LOCATION_UUID |
44c3efb0-…-1f756a03c0a1 |
Default location for synced records. |
OPENMRS_FHIR_EXTENSION_URL |
http://fhir.openmrs.org/ext/patient/identifier#location |
FHIR extension URL for identifier location. |
OpenMRS - order type UUIDs¶
CIS resolves these to internal order_type_ids at startup; they come from the Initializer
ordertypes.csv.
| Variable | Example value | Description |
|---|---|---|
OPENMRS_ORDER_TYPE_UUID_DRUG |
131168f4-…-000c29c2a5d7 |
Drug order type. |
OPENMRS_ORDER_TYPE_UUID_LAB |
52a447d3-…-50e549534c5e |
Lab order type. |
OPENMRS_ORDER_TYPE_UUID_RADIOLOGY |
c19c8e82-…-3f09890b2db3 |
Radiology order type. |
OPENMRS_ORDER_TYPE_UUID_PROCEDURE |
4237a01f-…-96d6e590aa33 |
Procedure order type. |
OPENMRS_ORDER_TYPE_UUID_MEDICAL_SUPPLY |
dab3ab30-…-8332a0831b49 |
Medical supply order type. |
OPENMRS_ORDER_TYPE_UUID_BED |
e8c4fd91-…-1a2b3c4d5e6f |
Bed order type (optional until CIS handles bed orders). |
Orthanc (PACS)¶
| Variable | Example value | Description |
|---|---|---|
ORTHANC_DB_NAME |
orthanc |
Orthanc PostgreSQL database name. |
ORTHANC_DB_USER |
orthanc |
Orthanc DB user. |
ORTHANC_DB_PASSWORD |
change-me-orthanc-db |
Orthanc DB password. |
ORTHANC_DB_HOST |
orthanc-db |
Orthanc DB service hostname. |
ORTHANC_USERNAME |
nidan |
HTTP Basic user for the Orthanc UI / CIS. |
ORTHANC_PASSWORD |
change-me-orthanc |
HTTP Basic password. |
ORTHANC_DICOM_UID_ROOT |
1.2.840.10008 |
DICOM UID root for generated instances. |
OpenELIS (Lab)¶
| Variable | Example value | Description |
|---|---|---|
OPENELIS_DOMAIN |
ehr.example.org |
Hostname OpenELIS serves under. |
OPENELIS_DB_NAME |
clinlims |
OpenELIS PostgreSQL database name. |
OPENELIS_DB_USER |
clinlims |
OpenELIS DB user. |
OPENELIS_DB_PASSWORD |
change-me-elis-db |
OpenELIS DB password. |
OPENELIS_DEFAULT_ADMIN_PASSWORD |
change-me-elis-admin |
Initial OpenELIS admin password. |
OPENELIS_TZ |
Asia/Kathmandu |
Container timezone. |
OPENELIS_ENABLED |
true |
Whether CIS talks to OpenELIS. |
OPENELIS_BASE_URL |
http://openelis:8080/OpenELIS-Global/rest |
OpenELIS REST base URL CIS calls. |
OPENELIS_USERNAME |
admin |
OpenELIS REST user for CIS. |
OPENELIS_PASSWORD |
change-me-elis-admin |
OpenELIS REST password. |
CIS_BASE_URL |
http://nidan-cis:8081 |
CIS URL OpenELIS calls back for sync. |
OPENELIS_MIDDLEWARE_TEST_SYNC_ENABLED |
false |
Sync test catalog OpenELIS→middleware. |
OPENELIS_MIDDLEWARE_RESULT_SYNC_ENABLED |
true |
Push lab results back through the middleware. |
OPENELIS_NIDAN_TESTORDER_ENABLED |
true |
Accept test orders from the middleware. |
OPENELIS_MIDDLEWARE_SYNC_SECRET |
change-me-elis-sync |
Shared secret for OpenELIS↔CIS sync calls. |
OPENELIS_NIDAN_PAYWALL_ENABLED |
true |
Gate result release on Odoo billing. |
NIDANCORE_ODOO_URL |
http://odoo:8069 |
Odoo URL the paywall checks. |
OPENELIS_NIDAN_PAYWALL_INSURANCE_BYPASS |
true |
Let insured patients bypass the paywall. |
Odoo (ERP)¶
Odoo owns billing, pharmacy, inventory, and POS. Note Odoo has two DB credential sets: the
application DB (ODOO_DB_*) and the PostgreSQL superuser (ODOO_POSTGRES_*).
| Variable | Example value | Description |
|---|---|---|
ODOO_DB_NAME |
nidan |
Odoo application database name. |
ODOO_DB_USER |
nidan |
Odoo application DB user. |
ODOO_DB_PASSWORD |
change-me-odoo-db |
Odoo application DB password. |
ODOO_POSTGRES_USER |
odoo |
PostgreSQL superuser. |
ODOO_POSTGRES_PASSWORD |
change-me-odoo-pg |
PostgreSQL superuser password. |
ODOO_POSTGRES_DB |
postgres |
Bootstrap/maintenance database. |
ODOO_DB_HOST |
odoo-db |
Odoo DB service hostname. |
ODOO_DB_PORT |
5432 |
Odoo DB port. |
ODOO_ADMIN_EMAIL |
admin@example.org |
Odoo master/admin login. |
ODOO_ADMIN_PASSWORD |
change-me-odoo-admin |
Odoo admin password. |
ODOO_WEB_BASE_URL |
https://ehr.example.org/odoo |
Public base URL Odoo generates links with. |
ODOO_WEB_BASE_URL_FREEZE |
True |
Prevent Odoo from auto-overwriting the base URL. |
ODOO_IMAGE |
trigonaltechnology/nidan_odoo_pro:stable |
Odoo image; use the Pro image to bake closed-source addons. |
ODOO_EXTRA_ADDONS_HOST_PATH |
../odoo_19_addons |
Dev-only: host path for open-source addons. |
ODOO_PRIVATE_ADDONS_HOST_PATH |
../nidan_odoo_extra_addons |
Dev-only: host path for private addons. |
ODOO_USERNAME |
admin |
Odoo user OIS uses for JSON-RPC. |
ODOO_PASSWORD |
change-me-odoo-admin |
Password for the OIS JSON-RPC user. |
Keycloak (identity)¶
| Variable | Example value | Description |
|---|---|---|
KEYCLOAK_DB_NAME |
keycloak |
Keycloak database name. |
KEYCLOAK_DB_USER |
keycloak |
Keycloak DB user. |
KEYCLOAK_DB_PASSWORD |
change-me-kc-db |
Keycloak DB password. |
KEYCLOAK_ADMIN_USER |
admin |
Keycloak admin console user. |
KEYCLOAK_ADMIN_PASSWORD |
change-me-kc-admin |
Keycloak admin password. |
KEYCLOAK_RELATIVE_PATH |
/auth |
Context path the gateway routes to Keycloak. |
Middleware database (CIS + OIS)¶
| Variable | Example value | Description |
|---|---|---|
INTEGRATION_DB_NAME |
nidan_integration |
Integration state database (CIS/OIS, Debezium offsets). |
INTEGRATION_DB_USER |
nidan |
Integration DB user. |
INTEGRATION_DB_PASSWORD |
change-me-int-db |
Integration DB password. |
INTEGRATION_DB_HOST |
nidan-integration-db |
Integration DB hostname. |
INTEGRATION_DB_PORT |
5432 |
Integration DB port. |
MANAGEMENT_HEALTH_REDIS_ENABLED |
false |
Include Redis in CIS/OIS health (set true only when Redis is deployed). |
Kafka & ZooKeeper¶
| Variable | Example value | Description |
|---|---|---|
ZOOKEEPER_CLIENT_PORT |
2181 |
ZooKeeper client port. |
ZOOKEEPER_TICK_TIME |
2000 |
ZooKeeper tick (ms). |
KAFKA_BROKER_ID |
1 |
Broker ID. |
KAFKA_PORT |
9092 |
Broker listener port. |
KAFKA_ZOOKEEPER_CONNECT |
nidan-zookeeper:2181 |
ZooKeeper connection string. |
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR |
1 |
Offsets topic replication (1 for single broker). |
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR |
1 |
Txn state log replication. |
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR |
1 |
Minimum in-sync replicas for txn log. |
KAFKA_MIN_INSYNC_REPLICAS |
1 |
Minimum in-sync replicas. |
CIS - Clinical Integration Service¶
| Variable | Example value | Description |
|---|---|---|
NIDAN_CIS_IMAGE |
trigonaltechnology/nidan-cis:latest |
CIS image (or nidan-cis:local for a local build). |
CIS_PORT |
8081 |
CIS HTTP port. |
CIS_KAFKA_GROUP_ID |
nidan-cis |
Kafka consumer group for CIS. |
OPENMRS_ENABLED |
true |
Enable the OpenMRS source. |
OPENMRS_BASE_URL |
http://openmrs-backend:8080/openmrs |
OpenMRS REST base URL. |
ORTHANC_ENABLED |
true |
Enable the Orthanc/radiology flow. |
ORTHANC_BASE_URL |
http://orthanc:8042 |
Orthanc base URL. |
ORTHANC_WORKLIST_DIR |
/var/lib/orthanc/worklists |
Where CIS writes MWL .wl files (mounted into Orthanc). |
DICOM_TYPE |
orthanc |
DICOM backend selector. |
INTEGRATION_SOURCE_SYSTEM |
CIS |
This service's source-system tag. |
INTEGRATION_SKIP_SOURCE_SYSTEMS |
OPENMRS |
Source systems whose own echoes CIS ignores (loop prevention). |
RADIOLOGY_ACCESSION_PREFIX |
RAD- |
Prefix for radiology accession numbers. |
RADIOLOGY_PROCEDURE_CODE_PREFIX |
RAD- |
Prefix for radiology procedure codes. |
INTEGRATION_FLOW_RADIOLOGY_MWL_ENABLED |
true |
Toggle the radiology MWL workflow. |
INTEGRATION_FLOW_RADIOLOGY_POS_ENABLED |
true |
Toggle radiology POS (Odoo) workflow. |
INTEGRATION_FLOW_LAB_OPENELIS_ENABLED |
true |
Toggle lab→OpenELIS workflow. |
INTEGRATION_FLOW_LAB_POS_ENABLED |
true |
Toggle lab POS workflow. |
DEBEZIUM_ENABLED |
true |
Enable embedded Debezium CDC from OpenMRS. |
DEBEZIUM_DB_USER |
root |
DB user Debezium reads the binlog as. |
DEBEZIUM_DB_PASSWORD |
change-me-omrs-root |
Password for the Debezium DB user. |
DEBEZIUM_TABLE_INCLUDE_LIST |
openmrs.visit,openmrs.orders,… |
Tables Debezium captures. |
INTEGRATION_KAFKA_RETRY_ATTEMPTS |
5 |
Kafka retry attempts before dead-lettering. |
INTEGRATION_KAFKA_RETRY_INITIAL_DELAY |
5s |
Initial retry backoff. |
INTEGRATION_KAFKA_RETRY_MAX_DELAY |
10m |
Maximum retry backoff. |
OIS - Order Integration Service¶
| Variable | Example value | Description |
|---|---|---|
NIDAN_OIS_IMAGE |
trigonaltechnology/nidan-ois:latest |
OIS image (or nidan-ois:local). |
OIS_PORT |
8082 |
OIS HTTP port. |
OIS_KAFKA_GROUP_ID |
nidan-ois |
Kafka consumer group for OIS. |
ODOO_BASE_URL |
http://odoo:8069 |
Odoo base URL for JSON-RPC. |
SKIP_SOURCE_SYSTEMS_FOR_ODOO |
ODOO |
Source systems OIS suppresses to Odoo (loop prevention). |
NIDAN_OIS_URL |
http://nidan-ois:8082 |
OIS URL the Odoo module calls. |
NIDAN_OIS_SECRET |
change-me-ois-secret |
Shared secret between Odoo and OIS. |
Keep the shared Odoo/OIS secrets aligned
NIDAN_OIS_SECRET, NIDAN_ODOO_SECRET, ODOO_NIDAN_SECRET, and OIS_WEBHOOK_SECRET are a set
of shared secrets between Odoo and OIS. Set them to the same dummy-replaced value on both
sides, or webhooks will be rejected.
DHIS2 / Federal HMIS reporting¶
| Variable | Example value | Description |
|---|---|---|
DHIS2_APP_PORT |
5000 |
Reporting app port. |
DHIS2_SECRET_KEY |
change-me-long-random-string |
Flask session secret. |
DHIS2_FLASK_ENV |
production |
Flask environment. |
DHIS2_ADMIN_PASSWORD |
change-me-dhis2-admin |
Reporting app admin password. |
DHIS2_REPORTER_USERNAME |
reporter |
Non-admin reporter login. |
DHIS2_REPORTER_PASSWORD |
change-me-dhis2-reporter |
Reporter password. |
DHIS2_DB_NAME |
nidan_dhis2 |
Reporting MySQL database. |
DHIS2_DB_USER |
nidan |
Reporting DB user. |
DHIS2_DB_PASSWORD |
change-me-dhis2-db |
Reporting DB password. |
DHIS2_DB_ROOT_PASSWORD |
change-me-dhis2-root |
Reporting MySQL root password. |
DHIS2_DB_HOST |
nidan-dhis2-db |
Reporting DB hostname. |
DHIS2_DB_PORT |
3306 |
Reporting DB port. |
FEDERAL_HMIS_URL |
https://hmis.gov.np/hmis |
Federal HMIS endpoint. |
FEDERAL_HMIS_USERNAME |
your-hospital-code |
Federal HMIS username. |
FEDERAL_HMIS_PASSWORD |
change-me-hmis |
Federal HMIS password. |
FEDERAL_HMIS_ORG_UNIT |
aBcD1234XyZ |
Federal HMIS org-unit UID. |
PROVINCIAL_DHIS2_URL |
https://dhis2.province.example/ |
Optional provincial DHIS2 endpoint. |
PROVINCIAL_DHIS2_USERNAME |
province-user |
Provincial DHIS2 username. |
PROVINCIAL_DHIS2_PASSWORD |
change-me-prov |
Provincial DHIS2 password. |
Superset (BI)¶
Superset reuses the OPENMRS_DB_*, OPENELIS_DB_*, and ODOO_DB_* values above to seed its
database connections.
| Variable | Example value | Description |
|---|---|---|
SUPERSET_SECRET_KEY |
change-me-long-random-string |
Superset session secret. |
SUPERSET_ADMIN_USERNAME |
admin |
Superset admin user. |
SUPERSET_ADMIN_PASSWORD |
change-me-superset-admin |
Superset admin password. |
SUPERSET_LOAD_EXAMPLES |
no |
Whether to load Superset example dashboards. |
SUPERSET_IMAGE |
trigonaltechnology/superset:latest |
Superset image. |
Verify the deployment¶
docker-compose ps # all services Up / healthy
docker-compose logs -f gateway # confirm routing and TLS
Then open https://<GATEWAY_DOMAIN>/ and sign in to each module with the credentials you set.
Database engines per system
In the current Compose file the databases are: OpenMRS → MariaDB 10.11; OpenELIS, Odoo,
Orthanc, and the integration store → PostgreSQL; DHIS2 reporting → MySQL 8. A
PostgreSQL option for OpenMRS exists in the Compose file but is commented out behind an
openmrs-postgres profile.