Add PostgreSQL with automated hot backup setup
Weekly full backups via pg_basebackup and continuous WAL archiving for point-in-time recovery, running as Docker Compose services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
.env
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# PostgreSQL with Hot Backup
|
||||||
|
|
||||||
|
PostgreSQL 18 with automated weekly full backups and continuous WAL archiving for point-in-time recovery.
|
||||||
|
|
||||||
|
## Services
|
||||||
|
|
||||||
|
| Service | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `postgres-db` | PostgreSQL 18 with WAL archiving enabled |
|
||||||
|
| `postgres-backup` | Sidecar that runs weekly full backups via `pg_basebackup` |
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env # edit with your credentials
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Environment variables in `.env`:
|
||||||
|
|
||||||
|
| Variable | Description | Default |
|
||||||
|
|----------|-------------|---------|
|
||||||
|
| `POSTGRES_USER` | Database user | `postgres` |
|
||||||
|
| `POSTGRES_PASSWORD` | Database password | — |
|
||||||
|
| `POSTGRES_DB` | Default database name | `postgres` |
|
||||||
|
| `PGPORT` | Host port for PostgreSQL | `5432` |
|
||||||
|
|
||||||
|
## Backup Strategy
|
||||||
|
|
||||||
|
### Full Backups (Weekly)
|
||||||
|
- `pg_basebackup` runs on startup, then every 7 days
|
||||||
|
- Stored in `/data/backup/postgres/full/full_<timestamp>/`
|
||||||
|
- Hot backup — no downtime required
|
||||||
|
|
||||||
|
### Incremental via WAL Archiving (Continuous)
|
||||||
|
- PostgreSQL archives Write-Ahead Log segments as they complete (~16MB each)
|
||||||
|
- Stored in `/data/backup/postgres/wal/`
|
||||||
|
- Captures every transaction between full backups
|
||||||
|
|
||||||
|
### Backup Location
|
||||||
|
|
||||||
|
```
|
||||||
|
/data/backup/postgres/
|
||||||
|
├── full/
|
||||||
|
│ └── full_2026-03-06_12-00-00/ # weekly full snapshots
|
||||||
|
└── wal/
|
||||||
|
├── 000000010000000000000001 # archived WAL segments
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Restore
|
||||||
|
|
||||||
|
To perform point-in-time recovery:
|
||||||
|
|
||||||
|
1. Stop PostgreSQL
|
||||||
|
2. Replace the data directory with a full backup:
|
||||||
|
```bash
|
||||||
|
cp -r /data/backup/postgres/full/full_<timestamp>/ /path/to/pgdata/
|
||||||
|
```
|
||||||
|
3. Create a `recovery.signal` file in the data directory
|
||||||
|
4. Configure `restore_command` in `postgresql.conf`:
|
||||||
|
```
|
||||||
|
restore_command = 'cp /data/backup/postgres/wal/%f %p'
|
||||||
|
```
|
||||||
|
5. Optionally set `recovery_target_time` for a specific point in time:
|
||||||
|
```
|
||||||
|
recovery_target_time = '2026-03-06 14:30:00'
|
||||||
|
```
|
||||||
|
6. Start PostgreSQL — it will replay WAL files and recover
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check backup service logs
|
||||||
|
docker compose logs postgres-backup
|
||||||
|
|
||||||
|
# Check WAL archiving activity
|
||||||
|
docker compose logs postgres-db | grep archive
|
||||||
|
|
||||||
|
# List full backups
|
||||||
|
ls /data/backup/postgres/full/
|
||||||
|
|
||||||
|
# List archived WAL files
|
||||||
|
ls /data/backup/postgres/wal/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Volumes
|
||||||
|
|
||||||
|
| Volume/Mount | Purpose |
|
||||||
|
|-------------|---------|
|
||||||
|
| `postgres_data` (Docker volume) | PostgreSQL data directory |
|
||||||
|
| `/data/backup/postgres/wal` | Archived WAL segments |
|
||||||
|
| `/data/backup/postgres` | All backups (full + WAL) |
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
services:
|
||||||
|
postgres-db:
|
||||||
|
image: postgres:18
|
||||||
|
container_name: postgres-db
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
ports:
|
||||||
|
- "${PGPORT}:5432"
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql
|
||||||
|
- /data/backup/postgres/wal:/backup/wal
|
||||||
|
- ./init-replication-hba.sh:/docker-entrypoint-initdb.d/init-replication-hba.sh:ro
|
||||||
|
command:
|
||||||
|
- postgres
|
||||||
|
- -c
|
||||||
|
- wal_level=replica
|
||||||
|
- -c
|
||||||
|
- archive_mode=on
|
||||||
|
- -c
|
||||||
|
- archive_command=test ! -f /backup/wal/%f && cp %p /backup/wal/%f
|
||||||
|
|
||||||
|
postgres-backup:
|
||||||
|
image: postgres:18
|
||||||
|
container_name: postgres-backup
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- postgres-db
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
PGPASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- /data/backup/postgres:/backup
|
||||||
|
entrypoint: ["/bin/bash", "-c"]
|
||||||
|
command:
|
||||||
|
- |
|
||||||
|
mkdir -p /backup/wal /backup/full
|
||||||
|
while true; do
|
||||||
|
STAMP=$$(date +%Y-%m-%d_%H-%M-%S)
|
||||||
|
echo "Starting full backup: full_$$STAMP"
|
||||||
|
pg_basebackup -h postgres-db -U "$$POSTGRES_USER" -D "/backup/full/full_$$STAMP" -Fp -Xs -P
|
||||||
|
echo "Full backup complete: full_$$STAMP"
|
||||||
|
sleep 604800
|
||||||
|
done
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Allow replication connections from the Docker network
|
||||||
|
echo "host replication all all scram-sha-256" >> "$PGDATA/pg_hba.conf"
|
||||||
Reference in New Issue
Block a user