Skip to content

Commit 637275a

Browse files
committed
Add postgres-backup docker image
0 parents  commit 637275a

File tree

11 files changed

+1016
-0
lines changed

11 files changed

+1016
-0
lines changed

.github/workflows/docker-build.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Build and Publish Docker Image
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- 'Dockerfile'
9+
- 'backup-script.sh'
10+
- '.github/workflows/docker-build.yml'
11+
release:
12+
types: [published]
13+
workflow_dispatch:
14+
15+
env:
16+
REGISTRY: ghcr.io
17+
IMAGE_NAME: ${{ github.repository }}
18+
19+
jobs:
20+
build-and-push:
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read
24+
packages: write
25+
26+
steps:
27+
- name: Checkout repository
28+
uses: actions/checkout@v4
29+
30+
- name: Set up Docker Buildx
31+
uses: docker/setup-buildx-action@v3
32+
33+
- name: Log in to Container Registry
34+
uses: docker/login-action@v3
35+
with:
36+
registry: ${{ env.REGISTRY }}
37+
username: ${{ github.actor }}
38+
password: ${{ secrets.GITHUB_TOKEN }}
39+
40+
- name: Extract metadata
41+
id: meta
42+
uses: docker/metadata-action@v5
43+
with:
44+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
45+
tags: |
46+
type=ref,event=branch
47+
type=ref,event=pr
48+
type=semver,pattern={{version}}
49+
type=semver,pattern={{major}}.{{minor}}
50+
type=semver,pattern={{major}}
51+
type=raw,value=latest,enable={{is_default_branch}}
52+
53+
- name: Build and push Docker image
54+
uses: docker/build-push-action@v5
55+
with:
56+
context: .
57+
platforms: linux/amd64,linux/arm64
58+
push: true
59+
tags: ${{ steps.meta.outputs.tags }}
60+
labels: ${{ steps.meta.outputs.labels }}
61+
cache-from: type=gha
62+
cache-to: type=gha,mode=max

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.env

Dockerfile

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Use Ubuntu as base image for better package management
2+
FROM ubuntu:24.04
3+
4+
# Metadata labels for the image
5+
LABEL org.opencontainers.image.title="PostgreSQL Backup Service"
6+
LABEL org.opencontainers.image.description="Automated PostgreSQL backup service with S3/R2 sync and retention policies"
7+
LABEL org.opencontainers.image.vendor="ServiceStack"
8+
LABEL org.opencontainers.image.licenses="MIT"
9+
LABEL org.opencontainers.image.source="https://github.com/ServiceStack/backup-postgres"
10+
LABEL org.opencontainers.image.documentation="https://github.com/ServiceStack/backup-postgres#readme"
11+
12+
# Avoid interactive prompts during package installation
13+
ENV DEBIAN_FRONTEND=noninteractive
14+
15+
# Add PostgreSQL official APT repository for latest version
16+
RUN apt-get update && apt-get install -y \
17+
curl \
18+
ca-certificates \
19+
gnupg \
20+
lsb-release \
21+
&& curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /usr/share/keyrings/postgresql-keyring.gpg \
22+
&& echo "deb [signed-by=/usr/share/keyrings/postgresql-keyring.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list
23+
24+
# Update package list and install required packages
25+
RUN apt-get update && apt-get install -y \
26+
# PostgreSQL client tools (latest version)
27+
postgresql-client-17 \
28+
# AWS CLI dependencies
29+
unzip \
30+
python3 \
31+
python3-pip \
32+
# Utilities
33+
cron \
34+
bash \
35+
findutils \
36+
&& rm -rf /var/lib/apt/lists/*
37+
38+
# Install AWS CLI v2
39+
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" \
40+
&& unzip awscliv2.zip \
41+
&& ./aws/install \
42+
&& rm -rf awscliv2.zip aws/
43+
44+
# Configure AWS CLI for R2 compatibility
45+
RUN mkdir -p /root/.aws \
46+
&& echo '[default]' > /root/.aws/config \
47+
&& echo 's3 =' >> /root/.aws/config \
48+
&& echo ' signature_version = s3v4' >> /root/.aws/config \
49+
&& echo ' addressing_style = path' >> /root/.aws/config \
50+
&& echo ' multipart_threshold = 1GB' >> /root/.aws/config \
51+
&& echo ' multipart_chunksize = 64MB' >> /root/.aws/config
52+
53+
# Create backup directory
54+
RUN mkdir -p /backups
55+
56+
# Create backup script
57+
COPY backup-script.sh /usr/local/bin/backup-script.sh
58+
RUN chmod +x /usr/local/bin/backup-script.sh
59+
60+
# Set working directory
61+
WORKDIR /backups
62+
63+
# Environment variables documentation
64+
ENV BACKUP_INTERVAL=86400 \
65+
RETENTION_DAYS=7 \
66+
S3_BUCKET="" \
67+
S3_PREFIX="db-backups" \
68+
S3_RETENTION_DAYS="" \
69+
POSTGRES_HOST="" \
70+
POSTGRES_USER="" \
71+
POSTGRES_PASSWORD="" \
72+
POSTGRES_DB="" \
73+
AWS_ACCESS_KEY_ID="" \
74+
AWS_SECRET_ACCESS_KEY="" \
75+
AWS_DEFAULT_REGION="" \
76+
AWS_ENDPOINT_URL=""
77+
78+
# Health check to verify the service is running
79+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
80+
CMD pgrep -f backup-script.sh > /dev/null || exit 1
81+
82+
# Default command
83+
CMD ["/usr/local/bin/backup-script.sh"]

README.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# PostgreSQL Backup Service
2+
3+
A containerized PostgreSQL backup service that automatically creates database backups with configurable retention policies and optional S3/R2 cloud storage sync.
4+
5+
## Features
6+
7+
- 🔄 **Automated Backups**: Configurable backup intervals (default: 24 hours)
8+
- 🗂️ **Retention Policies**: Automatic cleanup of old backups (local and cloud)
9+
- ☁️ **Cloud Storage**: Support for AWS S3 and Cloudflare R2
10+
- 🐳 **Container Ready**: Works with Docker Compose and Kamal accessories
11+
- 📊 **Health Checks**: Built-in health monitoring
12+
- 🔒 **Secure**: Uses environment variables for credentials
13+
14+
## Quick Start
15+
16+
### Using Docker Compose
17+
18+
1. Create a `.env` file with your database credentials:
19+
20+
```env
21+
POSTGRES_USER=myuser
22+
POSTGRES_PASSWORD=mypassword
23+
POSTGRES_DB=mydatabase
24+
25+
# Optional: S3/R2 configuration
26+
S3_BUCKET=my-backup-bucket
27+
S3_PREFIX=db-backups
28+
AWS_ACCESS_KEY_ID=your-access-key
29+
AWS_SECRET_ACCESS_KEY=your-secret-key
30+
AWS_DEFAULT_REGION=us-east-1
31+
32+
# For Cloudflare R2, also set:
33+
AWS_ENDPOINT_URL=https://your-account-id.r2.cloudflarestorage.com
34+
```
35+
36+
2. Use the provided `compose.yml`:
37+
38+
```bash
39+
docker compose up -d
40+
```
41+
42+
### Using Kamal Accessory
43+
44+
Add to your `config/deploy.yml`:
45+
46+
```yaml
47+
accessories:
48+
postgres-backup:
49+
image: ghcr.io/servicestack/backup-postgres:latest
50+
host: your-server
51+
env:
52+
clear:
53+
POSTGRES_HOST: postgres
54+
BACKUP_INTERVAL: "86400" # 24 hours
55+
RETENTION_DAYS: "7"
56+
S3_BUCKET: "your-backup-bucket"
57+
S3_PREFIX: "db-backups"
58+
S3_RETENTION_DAYS: "30"
59+
secret:
60+
- POSTGRES_USER
61+
- POSTGRES_PASSWORD
62+
- POSTGRES_DB
63+
- AWS_ACCESS_KEY_ID
64+
- AWS_SECRET_ACCESS_KEY
65+
- AWS_DEFAULT_REGION
66+
- AWS_ENDPOINT_URL # For R2
67+
volumes:
68+
- "/opt/backups:/backups"
69+
depends_on:
70+
- postgres
71+
```
72+
73+
## Configuration
74+
75+
### Environment Variables
76+
77+
| Variable | Required | Default | Description |
78+
|----------|----------|---------|-------------|
79+
| `POSTGRES_HOST` | ✅ | - | PostgreSQL server hostname |
80+
| `POSTGRES_USER` | ✅ | - | Database username |
81+
| `POSTGRES_PASSWORD` | ✅ | - | Database password |
82+
| `POSTGRES_DB` | ✅ | - | Database name |
83+
| `BACKUP_INTERVAL` | ❌ | `86400` | Backup interval in seconds |
84+
| `RETENTION_DAYS` | ❌ | `7` | Local backup retention in days |
85+
| `S3_BUCKET` | ❌ | - | S3/R2 bucket name |
86+
| `S3_PREFIX` | ❌ | `db-backups` | S3/R2 prefix/folder |
87+
| `S3_RETENTION_DAYS` | ❌ | - | Cloud backup retention in days |
88+
| `AWS_ACCESS_KEY_ID` | ❌ | - | AWS/R2 access key |
89+
| `AWS_SECRET_ACCESS_KEY` | ❌ | - | AWS/R2 secret key |
90+
| `AWS_DEFAULT_REGION` | ❌ | - | AWS region |
91+
| `AWS_ENDPOINT_URL` | ❌ | - | Custom endpoint (for R2) |
92+
93+
### Backup Schedule
94+
95+
- **Default**: Every 24 hours (`BACKUP_INTERVAL=86400`)
96+
- **Custom**: Set `BACKUP_INTERVAL` to any value in seconds
97+
- **Examples**:
98+
- Every 6 hours: `BACKUP_INTERVAL=21600`
99+
- Every 12 hours: `BACKUP_INTERVAL=43200`
100+
- Every hour: `BACKUP_INTERVAL=3600`
101+
102+
### Retention Policies
103+
104+
- **Local**: Controlled by `RETENTION_DAYS` (default: 7 days)
105+
- **Cloud**: Controlled by `S3_RETENTION_DAYS` (optional)
106+
- Cleanup runs after each backup
107+
108+
## Storage Options
109+
110+
### Local Storage Only
111+
112+
Set only the PostgreSQL connection variables. Backups will be stored locally with the configured retention policy.
113+
114+
### AWS S3
115+
116+
```env
117+
S3_BUCKET=my-backup-bucket
118+
AWS_ACCESS_KEY_ID=your-access-key
119+
AWS_SECRET_ACCESS_KEY=your-secret-key
120+
AWS_DEFAULT_REGION=us-east-1
121+
```
122+
123+
### Cloudflare R2
124+
125+
```env
126+
S3_BUCKET=my-backup-bucket
127+
AWS_ACCESS_KEY_ID=your-r2-access-key
128+
AWS_SECRET_ACCESS_KEY=your-r2-secret-key
129+
AWS_ENDPOINT_URL=https://your-account-id.r2.cloudflarestorage.com
130+
```
131+
132+
## Development
133+
134+
### Local Development
135+
136+
Use the development compose file:
137+
138+
```bash
139+
docker compose -f compose.dev.yml up -d
140+
```
141+
142+
This builds the image locally instead of pulling from the registry.
143+
144+
### Building the Image
145+
146+
```bash
147+
docker build -t backup-postgres .
148+
```
149+
150+
## Monitoring
151+
152+
### Health Checks
153+
154+
The container includes health checks that verify the backup service is running:
155+
156+
```bash
157+
docker ps # Check health status
158+
docker logs postgres_backup # View logs
159+
```
160+
161+
### Logs
162+
163+
The service provides detailed logging with timestamps:
164+
165+
```bash
166+
docker compose logs -f postgres_backup
167+
```
168+
169+
## Troubleshooting
170+
171+
### Common Issues
172+
173+
1. **Connection Failed**: Verify PostgreSQL credentials and network connectivity
174+
2. **S3 Upload Failed**: Check AWS credentials and bucket permissions
175+
3. **Permission Denied**: Ensure backup volume has proper write permissions
176+
177+
### Debug Mode
178+
179+
Add verbose logging by checking container logs:
180+
181+
```bash
182+
docker compose logs postgres_backup
183+
```
184+
185+
## License
186+
187+
MIT License - see LICENSE file for details.

0 commit comments

Comments
 (0)