Profiles & overlays
Compose overlays
The infra stack defaults small: Postgres + Valkey + your apps. Everything else
(observability, error tracking, queue dashboard, image-update detection, local
email catcher) is opt-in via a flag. Traefik runs in the prod profile only;
dev uses Vite’s dev-server proxy for same-origin DX. Composition happens in
dev.sh, which assembles the docker-compose invocation based on env vars.
1
base compose stack
5
opt-in overlays
2
stack profiles
How a stack is assembled
Section titled “How a stack is assembled”flowchart LR
base["base stack<br/>postgres · valkey<br/>api · ui"]
stack{"STACK= ?"}
dev["+ development-labels<br/>host ports for data services"]
prod["+ production-labels<br/>traefik · HTTPS · ACME · path routing"]
obs["+ observability"]
glitch["+ glitchtip"]
bullmq["+ bullmq (dev only)"]
wud["+ wud"]
mailpit["+ mailpit (dev only)"]
base --> stack
stack -->|dev| dev
stack -->|prod| prod
base -.->|WITH_OBSERVABILITY=1| obs
base -.->|WITH_GLITCHTIP=1| glitch
base -.->|WITH_BULLMQ=1| bullmq
base -.->|WITH_WUD=1| wud
base -.->|WITH_MAILPIT=1| mailpit
The result is a single docker compose -f ... -f ... --profile ... command. dev.sh is plain bash; you can read exactly what gets merged.
Design choices
Section titled “Design choices”Base stack always-on, everything else opt-in
First-time setup boots fast and uses minimal RAM.
Flags compose freely (WITH_OBSERVABILITY=1 WITH_GLITCHTIP=1 ...)
No combinatorial config files; each overlay is independent.
Separate dev / prod overlays for labels
HTTPS, ACME, and security headers live in prod-only files.
WITH_BULLMQ only valid in dev
Bull-board has no auth and no place in production.
Profiles + overlay files (not one giant file)
docker compose config stays readable; overlays can be skipped cleanly.
The full opt-in matrix
Section titled “The full opt-in matrix”WITH_OBSERVABILITY=1
Adds Prometheus, Grafana, Loki, Promtail, Alertmanager, and exporters.
WITH_GLITCHTIP=1
GlitchTip (Sentry-compatible error tracking); reuses base Postgres + Valkey.
WITH_BULLMQ=1
Bull-board UI at bullmq.localhost; dev only.
WITH_WUD=1
WUD watches container images. In prod, app images (api, ui) are
auto-deployed while base images remain notify-only; Discord/Slack webhooks
are optional.
WITH_MAILPIT=1
Mailpit SMTP catcher at :8025; dev only.
Combinations: WITH_OBSERVABILITY=1 WITH_GLITCHTIP=1 ./scripts/compose-up.sh is supported (and runs in CI).
STACK=dev vs STACK=prod
Section titled “STACK=dev vs STACK=prod”API + UI
dev: bind-mounted source, hot reload. prod: pre-built images pulled from GHCR.
Traefik
dev: not started; Vite dev-server proxies /api/* directly. prod:
started; terminates TLS, path-routes /api/* and /health to api,
everything else to ui.
Host(s)
dev: http://localhost:3001. prod: https://${PUBLIC_UI_HOST} (one
domain, same-origin).
TLS
dev: none. prod: Let’s Encrypt ACME via Traefik.
Data ports
dev: Postgres on :5432, Valkey on :6379 published to host. prod:
internal-only, not published.
STACK=prod adds Traefik and path-routing on top of the same data plane (Postgres + Valkey). No CORS in either profile.
Reading what’s running
Section titled “Reading what’s running”$ STACK=dev WITH_OBSERVABILITY=1 ./dev.sh config | less
$ ./dev.sh ps
# docker-compose.yml merged with docker-compose.observability.yml
ok postgres-dev is up on 5432
ok valkey-dev is up on 6379
ok api-dev is up on 3000
ok ui-dev is up on 3001./dev.sh forwards every argument to docker compose with the merged file list; so any compose command works (logs, exec, top, etc.).
Adding an overlay
Section titled “Adding an overlay”- Write a new
docker-compose.<name>.ymlwith the additional services. - Add a
WITH_<NAME>=1clause indev.shmirroring the existing ones. - Document the flag in
compose/.env.example. - Update the Commands cheatsheet.
Overlays are independent files, so you can ship one without touching the base.
Source
Section titled “Source”compose/dev.sh; the orchestrator. compose/docker-compose.*.yml; the base + overlays.
Related
Section titled “Related”- Infra overview; service inventory.
- Resource limits; sizing per service.
- Commands cheatsheet; every flag in one place.