# Run the published image end to end: the signed `ghcr.io` image plus its own # Postgres and Redis, a throwaway TLS certificate, and dev-only key files. # # This mirrors `compose.yaml`, but pulls `ghcr.io/syavne/d-oauth-server:latest` # instead of building from the checkout. Local evaluation only: fixed host # ports, self-signed TLS, placeholder secrets. Production guidance is in # docs/DEPLOYMENT.md. # # docker compose -f compose.ghcr.yaml up -d --pull always # docker compose -f compose.ghcr.yaml port app 8080 name: d-oauth-ghcr services: db: image: postgres:18-alpine environment: POSTGRES_USER: oauth POSTGRES_PASSWORD: oauth-dev-only POSTGRES_DB: oauth volumes: # postgres 18 keeps PGDATA under a major-version subdirectory, so the # volume mounts the parent (a mount on .../data is rejected by the image). - pgdata:/var/lib/postgresql healthcheck: test: ["CMD-SHELL", "pg_isready -U oauth -d oauth"] interval: 5s timeout: 3s retries: 20 redis: image: redis:8-alpine # The server asserts this contract at startup: noeviction + AOF. command: ["redis-server", "--appendonly", "yes", "--maxmemory-policy", "noeviction"] volumes: - redisdata:/data healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 5s timeout: 3s retries: 20 # One-shot: throwaway self-signed server cert for the local TLS listener. gencerts: image: alpine:3.21 volumes: - certs:/certs command: - sh - -ec - | apk add --no-cache openssl >/dev/null if [ ! -f /certs/tls.crt ]; then openssl req -x509 -newkey rsa:2048 -nodes -days 2 \ -keyout /certs/tls.key -out /certs/tls.crt \ -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" fi chown 65532:65532 /certs/tls.crt /certs/tls.key chmod 600 /certs/tls.key chmod 644 /certs/tls.crt # One-shot: DEV-ONLY pepper/TOTP/PII key files, shaped like the cluster's # 0400 files mounted at /run/secrets. Values are throwaway placeholders; # never reuse them outside this local compose file. gensecrets: image: alpine:3.21 volumes: - devsecrets:/run/secrets command: - sh - -ec - | if [ ! -f /run/secrets/token-pepper ]; then printf '%s' 'dev-only-pepper-ROTATE-IN-PROD-000000' > /run/secrets/token-pepper printf '%s' 'dev-only-totp-key-ROTATE-IN-PROD-000' > /run/secrets/totp-encryption-key printf '%s' 'dev-only-pii-key-ROTATE-IN-PROD-0000' > /run/secrets/pii-encryption-key fi chown 65532:65532 /run/secrets/token-pepper /run/secrets/totp-encryption-key /run/secrets/pii-encryption-key chmod 400 /run/secrets/token-pepper /run/secrets/totp-encryption-key /run/secrets/pii-encryption-key # Exactly-once migration (sqlx advisory lock). Replicas never migrate. migrate: image: ghcr.io/syavne/d-oauth-server:latest entrypoint: ["/d-oauth-server"] command: ["migrate"] depends_on: gencerts: condition: service_completed_successfully gensecrets: condition: service_completed_successfully db: condition: service_healthy redis: condition: service_healthy environment: D_OAUTH_ISSUER: https://localhost:8080 D_OAUTH_ENVIRONMENT: development D_OAUTH_BIND_ADDR: 0.0.0.0:8080 D_OAUTH_HEALTH_ADDR: 0.0.0.0:8081 D_OAUTH_TLS__CERT_PATH: /certs/tls.crt D_OAUTH_TLS__KEY_PATH: /certs/tls.key D_OAUTH_DATABASE__URL: postgres://oauth:oauth-dev-only@db:5432/oauth?sslmode=disable D_OAUTH_REDIS__URL: redis://redis:6379 D_OAUTH_REDIS__DURABILITY: waitaof D_OAUTH_TOKEN_PEPPER_FILE: /run/secrets/token-pepper D_OAUTH_TOTP_ENCRYPTION_KEY_FILE: /run/secrets/totp-encryption-key D_OAUTH_PII_ENCRYPTION_KEY_FILE: /run/secrets/pii-encryption-key volumes: - certs:/certs:ro - devsecrets:/run/secrets:ro app: image: ghcr.io/syavne/d-oauth-server:latest depends_on: migrate: condition: service_completed_successfully gensecrets: condition: service_completed_successfully db: condition: service_healthy redis: condition: service_healthy environment: D_OAUTH_ISSUER: https://localhost:8080 D_OAUTH_ENVIRONMENT: development D_OAUTH_BIND_ADDR: 0.0.0.0:8080 D_OAUTH_HEALTH_ADDR: 0.0.0.0:8081 D_OAUTH_TLS__CERT_PATH: /certs/tls.crt D_OAUTH_TLS__KEY_PATH: /certs/tls.key D_OAUTH_DATABASE__URL: postgres://oauth:oauth-dev-only@db:5432/oauth?sslmode=disable D_OAUTH_REDIS__URL: redis://redis:6379 D_OAUTH_REDIS__DURABILITY: waitaof D_OAUTH_TOKEN_PEPPER_FILE: /run/secrets/token-pepper D_OAUTH_TOTP_ENCRYPTION_KEY_FILE: /run/secrets/totp-encryption-key D_OAUTH_PII_ENCRYPTION_KEY_FILE: /run/secrets/pii-encryption-key # Fixed host ports, so the configured issuer (https://localhost:8080) # matches what you browse. If 8080 or 8081 is already taken, change the # mapping here and D_OAUTH_ISSUER together. ports: - "8080:8080" - "8081:8081" # plaintext health listener; local-only, never publish it read_only: true volumes: - certs:/certs:ro - devsecrets:/run/secrets:ro volumes: pgdata: redisdata: certs: devsecrets: