Repository navigation
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
190 lines (185 loc) · 5.68 KB
/
Copy pathdocker-compose.yml
File metadata and controls
190 lines (185 loc) · 5.68 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
services:
postgres:
image: postgres:15.19-alpine3.24
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
# Read by postgres-init/init-app-databases.sh — POSTGRES_DB only gets
# one database created for free; n8n and Baserow need their own.
N8N_DB_NAME: ${N8N_DB_NAME}
BASEROW_DB_NAME: ${BASEROW_DB_NAME}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./postgres-init/init-app-databases.sh:/docker-entrypoint-initdb.d/init-app-databases.sh:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
security_opt:
- no-new-privileges:true
# cap_drop/cap_add: official postgres entrypoint starts as root to fix
# data-directory ownership on first init, then drops via gosu. These five
# caps are the documented minimal set — verify a fresh-volume init still
# works cleanly before trusting this on a real deploy.
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- FOWNER
- SETUID
- SETGID
deploy:
resources:
limits:
memory: 512m
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- backend-network
n8n:
image: n8nio/n8n:2.44.4
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_DATABASE: ${N8N_DB_NAME}
DB_POSTGRESDB_USER: ${POSTGRES_USER}
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
WEBHOOK_URL: ${WEBHOOK_URL}
N8N_EMAIL_MODE: ${N8N_EMAIL_MODE}
N8N_SMTP_HOST: ${N8N_SMTP_HOST}
N8N_SMTP_PORT: ${N8N_SMTP_PORT}
N8N_SMTP_USER: ${N8N_SMTP_USER}
N8N_SMTP_PASS: ${N8N_SMTP_PASS}
GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
volumes:
- n8n_data:/home/node/.n8n
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:5678/healthz || exit 1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 30s
security_opt:
- no-new-privileges:true
# Safe to fully drop: the official image already runs as the
# unprivileged `node` user and never binds a privileged port.
cap_drop:
- ALL
deploy:
resources:
limits:
memory: 1g
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- proxy-network
- backend-network
baserow:
image: baserow/baserow:2.4.1
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres/${BASEROW_DB_NAME}
SECRET_KEY: ${BASEROW_SECRET_KEY}
volumes:
- baserow_data:/baserow/data
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost/api/_health/"]
# For docker compose ps / operator visibility only — CI's actual
# readiness gate is scripts/wait-for-healthy.sh, which polls this same
# check directly rather than trusting Docker's own Health.Status. That
# status was observed getting stuck on "unhealthy" under CI resource
# contention even while every request to this endpoint was returning
# 200, so it's deliberately not load-bearing for anything anymore.
# First boot on an empty volume runs Django migrations *and*
# Baserow's built-in template sync before this responds reliably —
# observed taking 150s+ on a GitHub Actions runner.
interval: 15s
timeout: 10s
retries: 10
start_period: 150s
security_opt:
- no-new-privileges:true
# No cap_drop here yet: Baserow's all-in-one image runs nginx +
# gunicorn/daphne + celery via an internal process supervisor whose root
# requirements aren't confirmed. Drop capabilities in a follow-up pass
# once this base hardening is proven in CI, not blindly in this one.
# 2g, not 1g: Baserow runs gunicorn + celery worker + celery beat
# concurrently, and first boot on an empty volume adds Django migrations
# + template-sync on top of that. Observed a celery worker getting
# OOM-killed (SIGKILL) under a 1g cap during exactly this burst on a
# GitHub Actions runner — steady-state usage is lower, but the limit has
# to cover the worst moment, not the average one.
deploy:
resources:
limits:
memory: 2g
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- proxy-network
- backend-network
caddy:
image: caddy:2.11.7-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
environment:
DOMAIN: ${DOMAIN}
N8N_SUBDOMAIN: ${N8N_SUBDOMAIN}
BASEROW_SUBDOMAIN: ${BASEROW_SUBDOMAIN}
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
security_opt:
- no-new-privileges:true
# NET_BIND_SERVICE is required to bind ports 80/443 as non-root.
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
deploy:
resources:
limits:
memory: 128m
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
- proxy-network
volumes:
postgres_data:
n8n_data:
baserow_data:
caddy_data:
caddy_config:
networks:
proxy-network:
driver: bridge
backend-network:
driver: bridge