# LOCAL development stack — runs with a plain `docker compose up`, no make / no # seed step / no bash. Use this on a local engine (Docker Desktop on Windows or # macOS, or rootless Podman on Linux). # # docker compose -f infra/docker-compose.local.yml up -d --build # podman # docker compose -f infra/docker-compose.local.yml up -d --build --wait # Docker Desktop # docker compose -f infra/docker-compose.local.yml down --volumes # # It is identical to infra/docker-compose.yml EXCEPT that the three config inputs # (OpenZaak data.yaml, Keycloak realms, Flowable BPMN) are **bind-mounted** from # the repo instead of being streamed into external volumes by infra/seed-config.sh. # Bind mounts work here because a local daemon can see your working directory — # the seed dance only exists for the containerized CI runner, where it can't. See # docs/runbooks/gitea-actions-gotchas.md. # # `infra/docker-compose.yml` remains the CI-canonical stack; keep the two in sync. # # Port map (host): # 8000 OpenZaak · 8001 Open Notificaties · 8080 BFF · 8090 Flowable REST # 8100 ACL · 8180 Keycloak (all admin: admin / admin — dev only) services: # ── OpenZaak (S-01) ────────────────────────────────────────────────────── oz-db: image: docker.io/postgis/postgis:17-3.5 environment: POSTGRES_USER: openzaak POSTGRES_PASSWORD: openzaak POSTGRES_DB: openzaak command: postgres -c max_connections=300 volumes: - oz-db:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U openzaak -d openzaak && psql -U openzaak -d openzaak -c 'SELECT PostGIS_Version();' -q 2>/dev/null"] interval: 5s timeout: 5s retries: 30 start_period: 15s networks: [cg] oz-redis: image: docker.io/library/redis:7 networks: [cg] oz-init: image: docker.io/openzaak/open-zaak:${OPENZAAK_TAG:-1.28.2} environment: &oz-env DJANGO_SETTINGS_MODULE: openzaak.conf.docker SECRET_KEY: ${OZ_SECRET_KEY:-dev-only-not-for-production} DB_HOST: oz-db DB_NAME: openzaak DB_USER: openzaak DB_PASSWORD: openzaak IS_HTTPS: "no" ALLOWED_HOSTS: "*" CACHE_DEFAULT: oz-redis:6379/0 CACHE_AXES: oz-redis:6379/0 CELERY_BROKER_URL: redis://oz-redis:6379/1 CELERY_RESULT_BACKEND: redis://oz-redis:6379/1 DISABLE_2FA: "true" # Publish notifications to NRC (always present in this twin). See ADR-0007. NOTIFICATIONS_DISABLED: "false" OPENZAAK_SUPERUSER_USERNAME: admin DJANGO_SUPERUSER_PASSWORD: admin OPENZAAK_SUPERUSER_EMAIL: admin@localhost RUN_SETUP_CONFIG: "true" command: /setup_configuration.sh # Bind mount (`:z` relabels for SELinux on Linux; a no-op on Docker Desktop). volumes: - ./openzaak/setup_configuration:/app/setup_configuration:ro,z depends_on: oz-db: condition: service_healthy oz-redis: condition: service_started networks: [cg] openzaak: image: docker.io/openzaak/open-zaak:${OPENZAAK_TAG:-1.28.2} environment: *oz-env healthcheck: test: ["CMD", "python", "-c", "import requests,sys; sys.exit(0 if requests.head('http://localhost:8000/admin/').status_code in (200,302) else 1)"] interval: 10s timeout: 5s retries: 10 start_period: 30s ports: - "8000:8000" depends_on: oz-init: condition: service_completed_successfully networks: [cg] oz-celery: image: docker.io/openzaak/open-zaak:${OPENZAAK_TAG:-1.28.2} environment: *oz-env command: /celery_worker.sh depends_on: oz-init: condition: service_completed_successfully networks: [cg] # ── Open Notificaties / NRC (S-01-c) ───────────────────────────────────── nrc-db: image: docker.io/postgis/postgis:17-3.5 environment: POSTGRES_USER: opennotificaties POSTGRES_PASSWORD: opennotificaties POSTGRES_DB: opennotificaties command: postgres -c max_connections=300 volumes: - nrc-db:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U opennotificaties -d opennotificaties"] interval: 5s timeout: 3s retries: 10 networks: [cg] nrc-redis: image: docker.io/library/redis:7 networks: [cg] nrc-init: # Migrations + setup_configuration (S-01-c): the JWT credential, Autorisaties-API # delegation, and the `zaken` kanaal that let OpenZaak publish. Config is # bind-mounted here (this twin is the local/no-make path). See ADR-0007. image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1} environment: &nrc-env DJANGO_SETTINGS_MODULE: nrc.conf.docker SECRET_KEY: ${NRC_SECRET_KEY:-dev-only-not-for-production} DB_HOST: nrc-db DB_NAME: opennotificaties DB_USER: opennotificaties DB_PASSWORD: opennotificaties IS_HTTPS: "no" ALLOWED_HOSTS: "*" CACHE_DEFAULT: nrc-redis:6379/0 CACHE_AXES: nrc-redis:6379/0 CELERY_BROKER_URL: redis://nrc-redis:6379/1 CELERY_RESULT_BACKEND: redis://nrc-redis:6379/1 DISABLE_2FA: "true" OPENNOTIFICATIES_SUPERUSER_USERNAME: admin DJANGO_SUPERUSER_PASSWORD: admin OPENNOTIFICATIES_SUPERUSER_EMAIL: admin@localhost RUN_SETUP_CONFIG: "true" NOTIFICATION_SEC_INTERVAL: "5" command: /setup_configuration.sh volumes: - ./opennotificaties/setup_configuration:/app/setup_configuration:ro,z depends_on: nrc-db: condition: service_healthy nrc-redis: condition: service_started openzaak: condition: service_healthy networks: [cg] nrc-web: image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1} environment: *nrc-env healthcheck: test: ["CMD", "python", "-c", "import requests,sys; sys.exit(0 if requests.head('http://localhost:8000/admin/').status_code in (200,302) else 1)"] interval: 10s timeout: 5s retries: 10 start_period: 30s ports: - "8001:8000" depends_on: nrc-init: condition: service_completed_successfully networks: [cg] nrc-celery: image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1} environment: *nrc-env command: /celery_worker.sh depends_on: nrc-init: condition: service_completed_successfully networks: [cg] # Celery beat drains scheduled notifications to subscribers — required for # delivery, not optional. See ADR-0007. nrc-beat: image: docker.io/openzaak/open-notificaties:${OPENNOTIFICATIES_TAG:-1.16.1} environment: *nrc-env command: /celery_beat.sh depends_on: nrc-init: condition: service_completed_successfully networks: [cg] # ── Keycloak (S-02) ────────────────────────────────────────────────────── keycloak: image: quay.io/keycloak/keycloak:26.1 command: ["start-dev", "--import-realm"] environment: KC_BOOTSTRAP_ADMIN_USERNAME: admin KC_BOOTSTRAP_ADMIN_PASSWORD: admin KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin KC_HEALTH_ENABLED: "true" KC_HTTP_ENABLED: "true" ports: - "8180:8080" volumes: - ./keycloak/realms:/opt/keycloak/data/import:ro,z networks: [cg] # ── Flowable (S-03) ────────────────────────────────────────────────────── flowable-db: image: docker.io/library/postgres:16 environment: POSTGRES_USER: flowable POSTGRES_PASSWORD: flowable POSTGRES_DB: flowable volumes: - flowable-db:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U flowable -d flowable"] interval: 5s timeout: 3s retries: 10 networks: [cg] flowable-rest: image: docker.io/flowable/flowable-rest:latest environment: SPRING_DATASOURCE_DRIVER-CLASS-NAME: org.postgresql.Driver SPRING_DATASOURCE_URL: jdbc:postgresql://flowable-db:5432/flowable SPRING_DATASOURCE_USERNAME: flowable SPRING_DATASOURCE_PASSWORD: flowable ports: - "8090:8080" depends_on: flowable-db: condition: service_healthy networks: [cg] flowable-init: image: docker.io/curlimages/curl:latest restart: "no" volumes: - ../workflows/registratie.bpmn:/work/registratie.bpmn:ro,z command: - sh - -c - | base=http://flowable-rest:8080/flowable-rest/service/repository/deployments until curl -sf -u rest-admin:test "$$base" >/dev/null 2>&1; do echo "waiting for flowable-rest..."; sleep 3; done if curl -s -u rest-admin:test "$$base?name=registratie" | grep -q '"name":"registratie"'; then echo "registratie already deployed; skip" else curl -sf -u rest-admin:test -F 'file=@/work/registratie.bpmn;filename=registratie.bpmn' "$$base" >/dev/null && echo "deployed registratie" fi depends_on: flowable-rest: condition: service_started networks: [cg] # ── ACL ────────────────────────────────────────────────────────────────── acl: build: context: ../services/acl dockerfile: Dockerfile image: register-referentie/acl:dev environment: Acl__OpenZaak__BaseUrl: http://openzaak:8000/ Acl__OpenZaak__ClientId: big-reference-seed Acl__OpenZaak__Secret: insecure-dev-secret-change-me Acl__Defaults__Bronorganisatie: "517439943" Acl__Defaults__VerantwoordelijkeOrganisatie: "517439943" Acl__Defaults__Vertrouwelijkheidaanduiding: openbaar Acl__Defaults__ZaaktypeUrl: ${ACL_ZAAKTYPE_URL:-http://openzaak:8000/catalogi/api/v1/zaaktypen/00000000-0000-0000-0000-000000000000} ports: - "8100:8080" healthcheck: test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"] interval: 5s timeout: 3s retries: 5 start_period: 10s depends_on: openzaak: condition: service_healthy networks: [cg] # ── BFF ────────────────────────────────────────────────────────────────── bff: build: context: ../services/bff dockerfile: Dockerfile image: register-referentie/bff:dev ports: - "8080:8080" healthcheck: test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"] interval: 5s timeout: 3s retries: 5 start_period: 10s networks: [cg] volumes: oz-db: nrc-db: flowable-db: networks: cg: