Docker Compose for Developers

1. Overview

EMS ships two kinds of Docker Compose usage:

  1. Per-service local dependencies — a MySQL + GreenMail stack that admin-service and portals talk to during mvn spring-boot:run on the host. Developers with Java tooling install admin-service or registration-portal locally, then run a Compose stack for the databases and SMTP.

  2. Gateway-only dev for frontend-first developers — an image that runs the portal’s Spring Boot gateway + pre-built Angular SPA against a dev admin-service deployed elsewhere (usually the shared dev cluster), so a Angular developer can work on the SPA without installing Java, Maven, or admin-service.

This page describes both. Neither replaces the Kubernetes-based deployment in CI or production; both are developer-only.

2. Stack 1: Local Dependencies

Location: admin-service/src/main/docker/ and registration-portal/src/main/docker/ (JHipster convention).

Typical contents:

# admin-service/src/main/docker/mysql.yml
services:
  mysql:
    image: mysql:8.0
    container_name: ems-mysql-dev
    environment:
      - MYSQL_USER=event-membership
      - MYSQL_PASSWORD=
      - MYSQL_DATABASE=event_admin_service
      - MYSQL_ALLOW_EMPTY_PASSWORD=yes
    ports: ['3306:3306']
    volumes:
      - ems-mysql-data:/var/lib/mysql
    command: >
      mysqld --lower_case_table_names=1
             --skip-mysqlx
             --character_set_server=utf8mb4
             --explicit_defaults_for_timestamp

volumes:
  ems-mysql-data:

Start:

cd admin-service
docker compose -f src/main/docker/mysql.yml up -d
./mvnw

The ./mvnw picks up application-dev.yml which points at localhost:3306. Profile gets auto-activated via the Maven docker-compose profile (see Maven POM Conventions).

2.1. GreenMail for SMTP

# admin-service/src/main/docker/greenmail.yml
services:
  greenmail:
    image: greenmail/standalone:2.1.0
    container_name: ems-greenmail-dev
    ports:
      - '3025:3025'  # SMTP
      - '3110:3110'  # POP3
      - '3143:3143'  # IMAP
      - '3465:3465'  # SMTPS
      - '3993:3993'  # IMAPS
      - '3995:3995'  # POP3S
      - '8080:8080'  # Web UI (browse inbox)
    environment:
      - GREENMAIL_OPTS=-Dgreenmail.setup.test.all -Dgreenmail.hostname=0.0.0.0 [email protected]:dev

See development/greenmail-email-server.adoc for the standing doc.

2.2. MailHog alternative

MailHog is lighter and has a prettier UI. If GreenMail’s footprint is a concern on dev laptops, substitute:

  mailhog:
    image: mailhog/mailhog:latest
    ports: ['1025:1025', '8025:8025']

Application-dev.yml adjusts SMTP to localhost:1025. Either works; GreenMail is more feature-complete (IMAP for automated tests); MailHog is the lazier dev option.

2.3. Keycloak for OIDC dev

When testing OIDC locally (admin-portal will need this frequently once WS5 starts):

  keycloak:
    image: quay.io/keycloak/keycloak:24.0
    command: start-dev
    environment:
      - KEYCLOAK_ADMIN=admin
      - KEYCLOAK_ADMIN_PASSWORD=admin
    ports: ['8180:8080']
    volumes:
      - ./keycloak-realm-dev.json:/opt/keycloak/data/import/realm.json:ro

Realm export file (keycloak-realm-dev.json) pre-seeded with an admin-portal-dev client registration and a few test users. Commit the realm file to admin-portal/src/main/docker/ so docker compose up produces a working OIDC setup on first try.

registration-portal’s existing application-oidc-dev.yml targets this Keycloak; admin-portal will point at the same one.

3. Stack 2: Gateway-Only Dev for Frontend Developers

Goal: enable a frontend developer with only Node installed (no Java, no Maven, no IDE) to clone the portal repo, npm install, npm start, and see a working SPA backed by a shared dev admin-service.

Mechanism:

  1. Portal publishes a pre-built Docker image containing the Spring Boot gateway without the Angular bundle — the gateway serves a placeholder index.html at / but proxies /services/admin-service/** to the configured admin-service URL and handles OIDC + session + API-key injection.

  2. Angular npm start runs ng serve on port 4200 (or BrowserSync on 9000) with a proxy config targeting the gateway container’s port 12506.

  3. The browser hits localhost:9000/services/admin-service/api/…​, BrowserSync proxies to localhost:12506/services/admin-service/api/…​, the gateway proxies to the shared dev admin-service with the right headers.

# admin-portal/src/main/docker/dev-gateway.yml
services:
  gateway-only:
    image: docker.io/christhonie/event-admin-portal:latest
    environment:
      - SPRING_PROFILES_ACTIVE=dev
      - APPLICATION_ADMIN_SERVICE_URL=https://dev-event-admin-service.idealogic.co.za
      - APPLICATION_ADMIN_SERVICE_API_KEY=${DEV_ADMIN_SERVICE_API_KEY}
      - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KEYCLOAK_ISSUER_URI=https://keycloak-dev.idealogic.co.za/realms/admin-portal-dev
      - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KEYCLOAK_CLIENT_ID=admin-portal-dev
      - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KEYCLOAK_CLIENT_SECRET=${DEV_OIDC_CLIENT_SECRET}
    ports:
      - '12506:12506'

The frontend developer then:

git clone admin-portal && cd admin-portal/src/main/webapp
npm install
# in another terminal:
docker compose -f ../../docker/dev-gateway.yml up -d
npm start  # ng serve with proxy.conf.js pointing at :12506

3.1. Proxy config for Angular dev server

admin-portal/src/main/webapp/webpack/proxy.conf.js routes the right paths to the gateway container:

module.exports = function ({ tls }) {
    const protocol = tls ? 'https' : 'http';
    return [
        {
            context: ['/services', '/api', '/oauth2', '/login', '/logout', '/management'],
            target: `${protocol}://localhost:12506`,
            secure: false,
            changeOrigin: true,
        },
    ];
};

Everything not starting with those prefixes (i.e. the SPA itself) is served by ng serve.

4. Caveats

4.1. Dev data

The shared dev admin-service has shared data. Frontend devs share a tenant pool; changes made in one browser are visible to all. Acceptable for UI development; not acceptable for end-to-end flow testing where clean state matters. For that, run admin-service locally (Stack 1) with an empty MySQL.

4.2. OIDC flow in gateway-only stack

The OIDC redirect from Keycloak to the gateway needs a reachable callback. localhost:12506 works if Keycloak is also running locally; if Keycloak is on a shared dev cluster, it must be configured to accept localhost:12506 as a redirect URI. Add http://localhost:12506/login/oauth2/code/keycloak to the client’s valid-redirect-URIs list.

4.3. API key rotation

If the shared dev admin-service key rotates, every frontend dev needs to update their DEV_ADMIN_SERVICE_API_KEY env var. Consider scripting this (a docker compose override file fetched from a private bucket on login) — but don’t commit keys.

5. What About Running Everything in Compose

A "full stack" Compose (admin-service + MySQL + portal + Keycloak + GreenMail) would replace the Kubernetes-based dev cluster for fully-local workflows. We don’t ship one today. Reasons:

  • admin-service has Liquibase migrations that take minutes against a clean database; users will kill containers before they’re ready.

  • Keeping a Compose file in sync with admin-service’s config changes is a maintenance burden that has burned us before.

  • Developers who need a real environment use idl-xnl-jhb-rc01 (shared dev cluster) via ArgoCD.

If a concrete pain point emerges (e.g. offline development during travel), revisit. Do not add a full-stack Compose pre-emptively.

6. Reference

File Role

admin-service/src/main/docker/mysql.yml

Local MySQL for admin-service dev

admin-service/src/main/docker/greenmail.yml

Local GreenMail for SMTP testing

registration-portal/src/main/docker/

Portal-side Compose files (if any)

development/greenmail-email-server.adoc

Standing doc for GreenMail usage

development/run-backend-local.adoc

End-to-end local backend setup

8. Change History

Date Change

2026-04-24

Initial draft. Documents both standing patterns (per-service local deps) and the proposed gateway-only dev mode for frontend-first developers.