forked from sbpp/sourcebans-pp
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathdocker-compose.prod.yml
More file actions
205 lines (194 loc) · 9.03 KB
/
docker-compose.prod.yml
File metadata and controls
205 lines (194 loc) · 9.03 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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# SourceBans++ production compose stack (#1381 deliverable 4).
#
# Designed for self-hosters who want a one-command "spin up a panel that
# survives reboots" deployment. Pulls the published image from GHCR;
# does NOT build from source. Pair with `.env.example.prod` (copy to
# `.env`, edit values, then `docker compose -f docker-compose.prod.yml
# up -d`).
#
# Distinct from `docker-compose.yml` (the dev stack — `./sbpp.sh up`):
# - dev image bind-mounts the worktree, ships admin/admin, exposes
# the DB to host, defines SBPP_DEV_KEEP_INSTALL → install/-presence
# guard skips. ALL of those are dangerous outside a developer's
# laptop.
# - this stack pulls an immutable image; reads admin creds from env
# vars (operator's responsibility); never exposes the DB; the
# entrypoint physically removes install/+updater/ from the
# writable layer so the panel-runtime guard passes without the
# dev escape hatch.
#
# Volumes:
# - dbdata — MariaDB data dir. Must persist (the panel's only
# source of truth for ban / admin / log rows).
# - demos — uploaded ban-evidence demos under web/demos/. Must
# persist (admin-uploaded user content; not regenerable).
# - cache — Smarty compile + sessions under web/cache/. Can be
# ephemeral; will rebuild on first request after wipe
# (operator just gets logged out, no data loss).
#
# config.php is rendered by the entrypoint into the image's writable
# layer at /var/www/html/web/config.php on first boot. To persist
# config.php across container recreations (rather than having the
# entrypoint mint a fresh SB_SECRET_KEY every time the layer is reset),
# set SB_SECRET_KEY in `.env` so the value survives. Volume-mounting
# config.php from outside the container is supported via the
# `SBPP_CONFIG_PATH` env var — see `.env.example.prod` for the
# Docker-secret pattern.
services:
web:
image: ghcr.io/sbpp/sourcebans-pp:${SBPP_IMAGE_TAG:-latest}
container_name: sbpp-prod-web
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports:
# Host: by default we bind to all interfaces. Self-hosters who
# front the panel with a reverse proxy (nginx, Caddy, Traefik,
# Cloudflare Tunnel) on the same host should set
# `SBPP_BIND=127.0.0.1:` so only the proxy can reach the panel
# — never the public internet. The trailing colon is intentional;
# it slots in front of the port mapping below.
- "${SBPP_BIND:-}${SBPP_HOST_PORT:-8080}:80"
environment:
# ----- Required: DB connection -----
# Either set DATABASE_URL OR the split DB_* vars below. The
# entrypoint parses DATABASE_URL into the split vars first; if
# either form is missing a field, the defaults below the URL
# parser kick in.
DATABASE_URL: "${DATABASE_URL:-}"
DB_HOST: "${DB_HOST:-db}"
DB_PORT: "${DB_PORT:-3306}"
DB_NAME: "${DB_NAME:-sourcebans}"
DB_USER: "${DB_USER:-sourcebans}"
DB_PASS: "${DB_PASS:?set DB_PASS in .env or use DB_PASS_FILE for a Docker-secret-mounted file}"
DB_PREFIX: "${DB_PREFIX:-sb}"
DB_CHARSET: "${DB_CHARSET:-utf8mb4}"
# ----- Required: panel security key -----
# Persist the JWT signing key across container recreations.
# Generate once with: openssl rand -base64 47
SB_SECRET_KEY: "${SB_SECRET_KEY:?run 'openssl rand -base64 47' and paste into .env so JWT cookies survive container restarts}"
# ----- Recommended: Steam Web API + admin email -----
STEAMAPIKEY: "${STEAMAPIKEY:-}"
SB_EMAIL: "${SB_EMAIL:-}"
# ----- First-boot install: initial admin -----
# The entrypoint's first-boot install pass seeds an Owner-flagged
# admin if all four are set; otherwise it skips and prints a
# next-step in the container logs (`docker compose logs web`).
INITIAL_ADMIN_NAME: "${INITIAL_ADMIN_NAME:-}"
INITIAL_ADMIN_STEAM: "${INITIAL_ADMIN_STEAM:-}"
INITIAL_ADMIN_EMAIL: "${INITIAL_ADMIN_EMAIL:-}"
INITIAL_ADMIN_PASSWORD: "${INITIAL_ADMIN_PASSWORD:-}"
# ----- App-platform / reverse-proxy aware -----
# PORT — when the app platform injects a port (Render / Fly /
# Heroku-style), the entrypoint rewrites Apache's Listen +
# vhost ports to match. Default 80 for plain Docker.
PORT: "${PORT:-80}"
# SBPP_TRUSTED_PROXIES — space-separated CIDR list of upstream
# proxies. When set, Apache uses mod_remoteip to rewrite
# REMOTE_ADDR to the original client IP from X-Forwarded-For,
# AND mirrors X-Forwarded-Proto into $_SERVER['HTTPS']. Empty
# by default so plain Docker deploys aren't trust-everyone.
# For Caddy / Traefik / nginx on the same compose network,
# the typical value is "172.16.0.0/12 10.0.0.0/8".
SBPP_TRUSTED_PROXIES: "${SBPP_TRUSTED_PROXIES:-}"
# SBPP_AUTO_INSTALL — set to "0" to opt OUT of the entrypoint's
# first-boot install pass (e.g. when pointing at a managed DB
# an operator already populated by hand). Default "1".
SBPP_AUTO_INSTALL: "${SBPP_AUTO_INSTALL:-1}"
# SBPP_CONFIG_PATH — when set, both the entrypoint's
# config.php writer AND the panel runtime's loader read/write
# config from this path. Used to mount config.php from a
# Docker-secret path outside the read-only image layer.
# Default: write into the image's writable layer at
# /var/www/html/web/config.php.
SBPP_CONFIG_PATH: "${SBPP_CONFIG_PATH:-}"
volumes:
# Demos: uploaded ban-evidence demos. MUST persist (admin
# uploads; not in DB, not regenerable from any other source).
- demos:/var/www/html/web/demos
# Cache: Smarty compile + PHP session files. Can be ephemeral —
# the panel rebuilds it on first request. Pinning to a named
# volume is purely a perf optimisation (compiled templates
# survive `docker compose down && up -d` cycles).
- cache:/var/www/html/web/cache
- smarty:/var/www/html/web/templates_c
healthcheck:
# The image's HEALTHCHECK fires every 30s; this block mirrors
# the same shape but with compose-style options. The
# container's HEALTHCHECK is the load-bearing one (Apache's
# status from inside the container); compose just surfaces
# it via `docker compose ps`.
test: ["CMD", "curl", "--fail", "--silent", "--max-time", "8", "http://127.0.0.1/health.php"]
interval: 30s
timeout: 10s
start_period: 120s
retries: 3
db:
image: mariadb:11
container_name: sbpp-prod-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: "${DB_ROOT_PASS:?set DB_ROOT_PASS in .env (only used for first-boot DB creation; the panel itself uses DB_USER/DB_PASS)}"
MYSQL_DATABASE: "${DB_NAME:-sourcebans}"
MYSQL_USER: "${DB_USER:-sourcebans}"
MYSQL_PASSWORD: "${DB_PASS:?set DB_PASS in .env}"
volumes:
# The DB data dir. THIS IS THE ONLY PATH THAT MUST SURVIVE
# `docker compose down -v` — wiping it loses every ban / admin /
# log row. Compose binds the named volume; back it up with
# `docker compose exec db mariadb-dump -uroot -p...`.
- dbdata:/var/lib/mysql
# NO `ports:` — the DB is on the compose network only. The web
# container reaches it via the service-name DNS (`db:3306`).
# Self-hosters who explicitly want host-port access uncomment
# the block below; the dev compose exposes 3307 for the same
# reason but in prod the surface should default to closed.
#
# ports:
# - "127.0.0.1:3307:3306"
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 5s
timeout: 5s
retries: 30
# -------- Reverse proxy (commented stub) --------
#
# Self-hosters who don't already run a reverse proxy on the host
# can uncomment the `caddy` block below to terminate TLS for the
# panel automatically (Let's Encrypt, no manual cert management).
# The Caddyfile is one line — `your.domain.tld { reverse_proxy
# web:80 }`. See `docker/caddy/Caddyfile.example` for the
# canonical shape.
#
# Operators with an existing nginx / Apache / HAProxy / Traefik
# in front of this compose stack do NOT need the Caddy service —
# they keep their existing chain and only add a single
# reverse_proxy directive pointing at the panel's published port.
#
# caddy:
# image: caddy:2
# container_name: sbpp-prod-caddy
# restart: unless-stopped
# depends_on:
# web:
# condition: service_healthy
# ports:
# - "80:80"
# - "443:443"
# - "443:443/udp"
# volumes:
# - ./docker/caddy/Caddyfile.example:/etc/caddy/Caddyfile:ro
# - caddy-data:/data
# - caddy-config:/config
# environment:
# # The Caddyfile reads $SBPP_DOMAIN to set the site address.
# SBPP_DOMAIN: "${SBPP_DOMAIN:?set SBPP_DOMAIN in .env if you uncomment the caddy service}"
volumes:
dbdata:
demos:
cache:
smarty:
# Uncomment alongside the caddy: service block above.
# caddy-data:
# caddy-config: