-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.prod.yml
More file actions
111 lines (107 loc) · 4.31 KB
/
Copy pathdocker-compose.prod.yml
File metadata and controls
111 lines (107 loc) · 4.31 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
# =============================================================================
# Atlas — Production Overlay
#
# Usage:
# docker compose -f docker-compose.yml -f docker-compose.prod.yml --profile backup up -d
#
# The `--profile backup` flag activates the db-backup service. In dev it's
# opt-in; in prod you almost always want it on.
#
# Assumes:
# - You provide a real .env (NOT the .example one)
# - TLS terminated upstream (Caddy, Traefik, nginx, or behind Tailscale)
# - data/ is on storage you actually trust and back up (rsync to your offsite target)
# =============================================================================
services:
postgres:
# Don't expose Postgres on the host in prod
ports: []
environment:
# Fail fast unless the operator set a real password in .env — never
# ship production on the `atlas` dev default. The base file's
# interpolation picks up the same value for the connection strings.
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set a strong POSTGRES_PASSWORD in .env for production}
# Conservative homelab defaults — tune to your host.
mem_limit: 512m
command:
- 'postgres'
- '-c'
- 'shared_buffers=256MB'
- '-c'
- 'max_connections=50'
# Runtime confinement (safe set — rootfs stays writable). The official
# Postgres entrypoint starts as root to fix PGDATA and socket-dir
# permissions and then drops to the `postgres` user, so it keeps a minimal
# capability allowlist rather than running fully unprivileged.
# no-new-privileges still applies: dropping privileges (gosu) is allowed;
# gaining them is not.
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN # chown PGDATA + socket dir to the postgres user
- DAC_OVERRIDE # adjust file perms during initdb
- FOWNER # chmod files it owns
- SETGID # drop root -> postgres group
- SETUID # drop root -> postgres user
pids_limit: 512
db-backup:
environment:
# Longer retention in prod: 90 days
DB01_CLEANUP_TIME: '129600'
# Archive after 30 days
DB01_ARCHIVE_TIME: '43200'
app:
build:
target: prod
# `!reset` drops the dev source mounts the base file declares — without it
# Compose CONCATENATES the lists and the dev src/public/node_modules/.next
# mounts shadow the baked standalone image. Re-add only the data mounts
# (documents = user-private; tiles = self-hosted basemap per ADR-0011,
# regenerable so :ro and excluded from backups).
volumes: !reset
- ./data/documents:/app/data/documents
- ./data/tiles:/app/data/tiles:ro
restart: always
# Conservative homelab default — tune to your host.
mem_limit: 1g
environment:
NODE_ENV: production
# Runtime confinement (safe set — rootfs stays writable). The app runs
# non-root (uid 1001, baked into the prod image) on a high port, so it
# needs no Linux capabilities at all.
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
pids_limit: 256
worker:
# Own hardened stage: keeps tsx + our source tree at runtime (the prod
# standalone strips both) and runs non-root.
build:
target: worker
# Pull the worker image — same tag as the app, suffixed `-worker`. The
# release publishes both from one version; ATLAS_IMAGE_TAG drives both.
image: ghcr.io/sebbogit/atlas:${ATLAS_IMAGE_TAG:-latest}-worker
# Override the base `pnpm worker` command — corepack as a non-root user can
# fail provisioning pnpm at runtime, so invoke tsx directly in prod.
command: ['node', '--import', 'tsx', 'scripts/worker.ts']
restart: always
# The worker runs document extraction, so it needs the documents mount
# (its storage adapter reads files off disk). `!reset` drops the dev
# source mounts inherited from the base; tiles stay app-only.
volumes: !reset
- ./data/documents:/app/data/documents
# Conservative homelab default — tune to your host.
mem_limit: 1g
environment:
NODE_ENV: production
# Runtime confinement (safe set — rootfs stays writable). The worker runs
# non-root (uid 1001, baked into the worker image) and needs no Linux
# capabilities.
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
pids_limit: 256