Repository navigation
Expand file tree
/
Copy pathdocker-compose.prod.yml
More file actions
128 lines (124 loc) · 4.93 KB
/
Copy pathdocker-compose.prod.yml
File metadata and controls
128 lines (124 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# Bounded container logs (#432). Docker's default json-file driver never
# rotates, so on the single prod EC2 host the logs of a long-lived container
# grow until the disk fills. 5 × 50 MB per service is days of normal traffic.
x-logging: &default-logging
driver: json-file
options:
max-size: "50m"
max-file: "5"
services:
reverse-proxy:
image: nginx:alpine
container_name: sa-reverse-proxy
restart: unless-stopped
logging: *default-logging
depends_on:
sa-web:
condition: service_healthy
sa-engine:
condition: service_healthy
volumes:
- ./docker/nginx.prod.conf:/etc/nginx/nginx.conf:ro
- ./docker/maintenance.html:/usr/share/nginx/html/maintenance.html:ro
healthcheck:
test: ["CMD-SHELL", "wget -qO /dev/null http://127.0.0.1:80/ || exit 1"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s
networks:
stackalchemist-prod:
# `reverse-proxy` alias keeps the Cloudflare Tunnel ingress working
# regardless of whether `container_name` carries the `sa-` prefix.
# Prevents Error 1033/502 drift between compose and CF dashboard.
aliases:
- reverse-proxy
sa-web:
build:
context: .
target: web
args:
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL:-https://stackalchemist.app}
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: ${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY}
NEXT_PUBLIC_PLAUSIBLE_DOMAIN: ${NEXT_PUBLIC_PLAUSIBLE_DOMAIN:-}
container_name: sa-web
restart: unless-stopped
logging: *default-logging
environment:
NODE_ENV: production
PORT: 3000
HOSTNAME: 0.0.0.0
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL:-https://stackalchemist.app}
NEXT_PUBLIC_IS_TEST_SITE: ${NEXT_PUBLIC_IS_TEST_SITE:-false}
ENGINE_API_URL: ${ENGINE_API_URL:-http://sa-engine}
# qavren-db (Postgres store) and Qavren Auth; the deploy preflight requires both.
DATABASE_URL: ${DATABASE_URL:-}
QAVREN_AUTH_URL: ${QAVREN_AUTH_URL:-}
QAVREN_REALM: ${QAVREN_REALM:-stackalchemist}
AUTH_SECRET: ${AUTH_SECRET:-}
# Auth.js needs the public origin behind the reverse proxy (request.url is the container
# bind address and X-Forwarded-Proto is http). Origin only, never a path.
AUTH_URL: ${NEXT_PUBLIC_APP_URL:-https://stackalchemist.app}
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY}
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: ${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY}
ENGINE_SERVICE_KEY: ${ENGINE_SERVICE_KEY:-}
# BYOK: web encrypts stored per-user API keys with this secret.
BYOK_ENCRYPTION_KEY: ${BYOK_ENCRYPTION_KEY:-}
# SEO / Analytics — only populated on prod, empty elsewhere.
NEXT_PUBLIC_PLAUSIBLE_DOMAIN: ${NEXT_PUBLIC_PLAUSIBLE_DOMAIN:-}
GOOGLE_SITE_VERIFICATION: ${GOOGLE_SITE_VERIFICATION:-}
healthcheck:
# /api/healthz is exempt from Basic Auth; keeps probes healthy across envs.
test: ["CMD-SHELL", "wget -qO /dev/null http://localhost:3000/api/healthz || exit 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
networks:
- stackalchemist-prod
sa-engine:
build:
context: .
target: engine
container_name: sa-engine
restart: unless-stopped
init: true
# NET_ADMIN lets the entrypoint install the build user's egress firewall (#454); it is
# dropped from the Engine's bounding set before the Engine starts.
cap_add:
- NET_ADMIN
logging: *default-logging
environment:
ASPNETCORE_ENVIRONMENT: ${ASPNETCORE_ENVIRONMENT:-Production}
# Refuse to start without the build egress firewall (docker/engine/entrypoint.sh).
SA_BUILD_EGRESS: required
ASPNETCORE_URLS: http://+:80
API_PORT: ${API_PORT:-5000}
# qavren-db (Postgres store); required in Production.
DATABASE_URL: ${DATABASE_URL:-}
R2_ACCOUNT_ID: ${R2_ACCOUNT_ID}
R2_ACCESS_KEY_ID: ${R2_ACCESS_KEY_ID}
R2_SECRET_ACCESS_KEY: ${R2_SECRET_ACCESS_KEY}
R2_BUCKET_NAME: ${R2_BUCKET_NAME:-stackalchemist-generations-prod}
R2_S3_URL: ${R2_S3_URL}
R2_ENDPOINT: ${R2_ENDPOINT}
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY}
STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET}
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: ${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-claude-sonnet-5-5}
ENGINE_SERVICE_KEY: ${ENGINE_SERVICE_KEY:-}
# BYOK: engine decrypts stored per-user API keys with this secret (falls
# back to ENGINE_SERVICE_KEY for keys saved before the distinct-key rule).
BYOK_ENCRYPTION_KEY: ${BYOK_ENCRYPTION_KEY:-}
healthcheck:
test: ["CMD-SHELL", "wget -qO /dev/null http://localhost:80/healthz || exit 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
networks:
- stackalchemist-prod
networks:
stackalchemist-prod:
name: stackalchemist-prod