-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
145 lines (137 loc) · 5.93 KB
/
Copy pathdocker-compose.yml
File metadata and controls
145 lines (137 loc) · 5.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# =============================================================================
# BackupHelper — Standalone Example (PostgreSQL + uploads → local + off-site S3)
# =============================================================================
# Usage: cp .env.example .env && edit .env && docker compose up -d
# docker compose run --rm backup --now # run one snapshot now
# docker compose run --rm backup list # list snapshots
# docker compose run --rm backup verify <id>
#
# What this shows:
# - The whole backup job passed INLINE as BACKUP_CONFIG_JSON — no host config
# file needed. Secrets are referenced as $${VAR} so Compose leaves them
# literal and the container resolves them from its own environment (they
# never end up baked into the rendered config).
# - One job with two sources (Postgres dump + an uploads directory) bundled
# into a single atomic snapshot, kept locally AND pushed off-site to any
# S3-compatible target (leave BACKUP_S3_* empty to stay local-only).
#
# Architecture:
# ┌───────────┐ ┌──────────────┐ ┌──────────────────┐
# │ database │◀──────▶│ backup │──────▶ │ local /data volume│
# │ (Postgres)│ dump │ (BackupHelper)│ +off- └──────────────────┘
# └───────────┘ └──────┬───────┘ site ┌──────────────────┐
# └────────────────▶│ S3-compatible bkt │
# └──────────────────┘
# =============================================================================
x-logging: &logging
logging:
driver: json-file
options:
max-size: "50m"
max-file: "3"
services:
# ── PostgreSQL (the database being backed up) ──────────────────────────────
database:
image: ${POSTGRES_IMAGE:-postgres}:${POSTGRES_VERSION:-18-alpine}
container_name: ${STACK_NAME:-app}_DATABASE
hostname: database
restart: unless-stopped
<<: *logging
environment:
TZ: ${TIME_ZONE:-Etc/UTC}
POSTGRES_DB: ${DB_NAME:-app}
POSTGRES_USER: ${DB_USER:-app}
POSTGRES_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD in .env}
volumes:
- database-data:/var/lib/postgresql
expose:
- 5432/tcp
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-app} -d ${DB_NAME:-app}"]
interval: 15s
timeout: 10s
retries: 5
start_period: 30s
networks:
- local
# ── BackupHelper (the backup engine) ───────────────────────────────────────
backup:
image: ${BACKUP_IMAGE:-ghcr.io/bauer-group/cs-backuphelper/backuphelper}:${BACKUP_VERSION:-latest}
container_name: ${STACK_NAME:-app}_BACKUP
hostname: backup
profiles: ["backup"]
restart: unless-stopped
<<: *logging
depends_on:
database:
condition: service_healthy
environment:
TZ: ${TIME_ZONE:-Etc/UTC}
BACKUP_DATA_DIR: /data
BACKUP_LOG_LEVEL: ${BACKUP_LOG_LEVEL:-INFO}
BACKUP_LOG_FORMAT: ${BACKUP_LOG_FORMAT:-console}
# Secrets resolved by the container (kept out of the rendered config).
DB_PASSWORD: ${DB_PASSWORD}
BACKUP_S3_SECRET_KEY: ${BACKUP_S3_SECRET_KEY:-}
WEBHOOK_SECRET: ${BACKUP_WEBHOOK_SECRET:-}
# The whole job, inline. $${VAR} stays literal in Compose and is resolved
# by BackupHelper from the environment above.
BACKUP_CONFIG_JSON: |
{
"instance_name": "${STACK_NAME:-app}",
"jobs": [{
"name": "main",
"sources": [
{"type": "postgres", "host": "database", "port": 5432,
"database": "${DB_NAME:-app}", "user": "${DB_USER:-app}",
"password": "$${DB_PASSWORD}", "dump_format": "custom"},
{"type": "filesystem", "name": "uploads", "path": "/uploads",
"exclude": ["cache/*", "tmp/*"]}
],
"destinations": [
{"type": "local"},
{"type": "s3",
"endpoint": "${BACKUP_S3_ENDPOINT:-}",
"bucket": "${BACKUP_S3_BUCKET:-}",
"access_key": "${BACKUP_S3_ACCESS_KEY:-}",
"secret_key": "$${BACKUP_S3_SECRET_KEY}",
"region": "${BACKUP_S3_REGION:-eu-central-1}",
"prefix": "${BACKUP_S3_PREFIX:-app/}"}
],
"schedule": {"mode": "cron", "cron": "${BACKUP_CRON:-15 3 * * *}",
"on_startup": ${BACKUP_ON_STARTUP:-false}},
"retention": {"count": ${BACKUP_RETENTION_COUNT:-14},
"age_days": ${BACKUP_RETENTION_AGE_DAYS:-90}},
"encryption": {"mode": "${BACKUP_ENCRYPTION_MODE:-none}"},
"notifications": {
"channels": ${BACKUP_ALERT_CHANNELS:-[]},
"level": "${BACKUP_ALERT_LEVEL:-warnings}",
"webhook": {"url": "${BACKUP_WEBHOOK_URL:-}", "secret": "$${WEBHOOK_SECRET}"},
"teams": {"url": "${BACKUP_TEAMS_WEBHOOK:-}"}
}
}]
}
volumes:
- backup-data:/data
- ${UPLOADS_PATH:-app-uploads}:/uploads:ro
healthcheck:
test: ["CMD", "backuphelper", "healthcheck"]
interval: 60s
timeout: 10s
retries: 3
start_period: 20s
deploy:
resources:
limits:
cpus: "${BACKUP_CPU_LIMIT:-1.0}"
memory: ${BACKUP_MEM_LIMIT:-512M}
networks:
- local
networks:
local:
driver: bridge
name: ${STACK_NAME:-app}
volumes:
database-data:
backup-data:
app-uploads: