compose.ghcr.yaml 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. # Run the published image end to end: the signed `ghcr.io` image plus its own
  2. # Postgres and Redis, a throwaway TLS certificate, and dev-only key files.
  3. #
  4. # This mirrors `compose.yaml`, but pulls `ghcr.io/syavne/d-oauth-server:latest`
  5. # instead of building from the checkout. Local evaluation only: fixed host
  6. # ports, self-signed TLS, placeholder secrets. Production guidance is in
  7. # docs/DEPLOYMENT.md.
  8. #
  9. # docker compose -f compose.ghcr.yaml up -d --pull always
  10. # docker compose -f compose.ghcr.yaml port app 8080
  11. name: d-oauth-ghcr
  12. services:
  13. db:
  14. image: postgres:18-alpine
  15. environment:
  16. POSTGRES_USER: oauth
  17. POSTGRES_PASSWORD: oauth-dev-only
  18. POSTGRES_DB: oauth
  19. volumes:
  20. # postgres 18 keeps PGDATA under a major-version subdirectory, so the
  21. # volume mounts the parent (a mount on .../data is rejected by the image).
  22. - pgdata:/var/lib/postgresql
  23. healthcheck:
  24. test: ["CMD-SHELL", "pg_isready -U oauth -d oauth"]
  25. interval: 5s
  26. timeout: 3s
  27. retries: 20
  28. redis:
  29. image: redis:8-alpine
  30. # The server asserts this contract at startup: noeviction + AOF.
  31. command: ["redis-server", "--appendonly", "yes", "--maxmemory-policy", "noeviction"]
  32. volumes:
  33. - redisdata:/data
  34. healthcheck:
  35. test: ["CMD", "redis-cli", "ping"]
  36. interval: 5s
  37. timeout: 3s
  38. retries: 20
  39. # One-shot: throwaway self-signed server cert for the local TLS listener.
  40. gencerts:
  41. image: alpine:3.21
  42. volumes:
  43. - certs:/certs
  44. command:
  45. - sh
  46. - -ec
  47. - |
  48. apk add --no-cache openssl >/dev/null
  49. if [ ! -f /certs/tls.crt ]; then
  50. openssl req -x509 -newkey rsa:2048 -nodes -days 2 \
  51. -keyout /certs/tls.key -out /certs/tls.crt \
  52. -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
  53. fi
  54. chown 65532:65532 /certs/tls.crt /certs/tls.key
  55. chmod 600 /certs/tls.key
  56. chmod 644 /certs/tls.crt
  57. # One-shot: DEV-ONLY pepper/TOTP/PII key files, shaped like the cluster's
  58. # 0400 files mounted at /run/secrets. Values are throwaway placeholders;
  59. # never reuse them outside this local compose file.
  60. gensecrets:
  61. image: alpine:3.21
  62. volumes:
  63. - devsecrets:/run/secrets
  64. command:
  65. - sh
  66. - -ec
  67. - |
  68. if [ ! -f /run/secrets/token-pepper ]; then
  69. printf '%s' 'dev-only-pepper-ROTATE-IN-PROD-000000' > /run/secrets/token-pepper
  70. printf '%s' 'dev-only-totp-key-ROTATE-IN-PROD-000' > /run/secrets/totp-encryption-key
  71. printf '%s' 'dev-only-pii-key-ROTATE-IN-PROD-0000' > /run/secrets/pii-encryption-key
  72. fi
  73. chown 65532:65532 /run/secrets/token-pepper /run/secrets/totp-encryption-key /run/secrets/pii-encryption-key
  74. chmod 400 /run/secrets/token-pepper /run/secrets/totp-encryption-key /run/secrets/pii-encryption-key
  75. # Exactly-once migration (sqlx advisory lock). Replicas never migrate.
  76. migrate:
  77. image: ghcr.io/syavne/d-oauth-server:latest
  78. entrypoint: ["/d-oauth-server"]
  79. command: ["migrate"]
  80. depends_on:
  81. gencerts:
  82. condition: service_completed_successfully
  83. gensecrets:
  84. condition: service_completed_successfully
  85. db:
  86. condition: service_healthy
  87. redis:
  88. condition: service_healthy
  89. environment:
  90. D_OAUTH_ISSUER: https://localhost:8080
  91. D_OAUTH_ENVIRONMENT: development
  92. D_OAUTH_BIND_ADDR: 0.0.0.0:8080
  93. D_OAUTH_HEALTH_ADDR: 0.0.0.0:8081
  94. D_OAUTH_TLS__CERT_PATH: /certs/tls.crt
  95. D_OAUTH_TLS__KEY_PATH: /certs/tls.key
  96. D_OAUTH_DATABASE__URL: postgres://oauth:oauth-dev-only@db:5432/oauth?sslmode=disable
  97. D_OAUTH_REDIS__URL: redis://redis:6379
  98. D_OAUTH_REDIS__DURABILITY: waitaof
  99. D_OAUTH_TOKEN_PEPPER_FILE: /run/secrets/token-pepper
  100. D_OAUTH_TOTP_ENCRYPTION_KEY_FILE: /run/secrets/totp-encryption-key
  101. D_OAUTH_PII_ENCRYPTION_KEY_FILE: /run/secrets/pii-encryption-key
  102. volumes:
  103. - certs:/certs:ro
  104. - devsecrets:/run/secrets:ro
  105. app:
  106. image: ghcr.io/syavne/d-oauth-server:latest
  107. depends_on:
  108. migrate:
  109. condition: service_completed_successfully
  110. gensecrets:
  111. condition: service_completed_successfully
  112. db:
  113. condition: service_healthy
  114. redis:
  115. condition: service_healthy
  116. environment:
  117. D_OAUTH_ISSUER: https://localhost:8080
  118. D_OAUTH_ENVIRONMENT: development
  119. D_OAUTH_BIND_ADDR: 0.0.0.0:8080
  120. D_OAUTH_HEALTH_ADDR: 0.0.0.0:8081
  121. D_OAUTH_TLS__CERT_PATH: /certs/tls.crt
  122. D_OAUTH_TLS__KEY_PATH: /certs/tls.key
  123. D_OAUTH_DATABASE__URL: postgres://oauth:oauth-dev-only@db:5432/oauth?sslmode=disable
  124. D_OAUTH_REDIS__URL: redis://redis:6379
  125. D_OAUTH_REDIS__DURABILITY: waitaof
  126. D_OAUTH_TOKEN_PEPPER_FILE: /run/secrets/token-pepper
  127. D_OAUTH_TOTP_ENCRYPTION_KEY_FILE: /run/secrets/totp-encryption-key
  128. D_OAUTH_PII_ENCRYPTION_KEY_FILE: /run/secrets/pii-encryption-key
  129. # Fixed host ports, so the configured issuer (https://localhost:8080)
  130. # matches what you browse. If 8080 or 8081 is already taken, change the
  131. # mapping here and D_OAUTH_ISSUER together.
  132. ports:
  133. - "8080:8080"
  134. - "8081:8081" # plaintext health listener; local-only, never publish it
  135. read_only: true
  136. volumes:
  137. - certs:/certs:ro
  138. - devsecrets:/run/secrets:ro
  139. volumes:
  140. pgdata:
  141. redisdata:
  142. certs:
  143. devsecrets: