Scheduled PostgreSQL backups to S3-compatible storage, packaged as a
non-root Docker image. backupctl creates custom-format dumps, verifies every
archive before upload, and can perform an isolated restore test — without a
Docker socket.
Images are published on Docker Hub as
jesulonimii/postgres-s3-backup.
pg_dump custom format, then checks every archive with pg_restore --list.Remote backup deletion is deliberately not supported. Set retention with a bucket lifecycle rule instead.
Create config.yaml with the connection and storage settings. Replace every
placeholder before starting the container:
database:
url: postgresql://backup_user:[email protected]:5432/example?sslmode=verify-full
s3:
endpoint: https://s3.example.com
region: auto
bucket: example-db-backups
access_key_id: change-me
secret_access_key: change-me
force_path_style: false
server_side_encryption: AES256
backup:
prefix: staging
schedule: "0 2 * * *"
verify_schedule: "0 3 1 * *"
timezone: Africa/Lagos
retention_days: 14
# Uncomment all mail values to send alerts on failure.
# mail:
# smtp_url: smtps://username:[email protected]:465
# from: [email protected]
# to: [email protected]
Then create compose.yml alongside it:
services:
backup:
image: jesulonimii/postgres-s3-backup:1.0.0
restart: unless-stopped
read_only: true
tmpfs:
- /var/lib/backupctl/tmp:mode=1777
security_opt:
- no-new-privileges:true
volumes:
- ./config.yaml:/etc/backupctl/config.yaml:ro
Start the scheduler:
docker compose up -d
The default command is daemon. It runs the backup and verification schedules
in the configured timezone. The complete config file is also available as
config.example.yaml.
Use the same Compose configuration for one-off commands:
docker compose run --rm backup run
docker compose run --rm backup verify
docker compose run --rm backup status
status returns redacted JSON. verify downloads the newest successful
archive under BACKUP_PREFIX, validates its checksum, and restores it to a
temporary in-container PostgreSQL instance.
The grouped YAML file above is read from /etc/backupctl/config.yaml. Set
CONFIG_FILE to use a different path. The former flat YAML keys, such as
database_url and s3_bucket, remain supported for compatibility.
Environment variables are an alternative configuration method and override
YAML values when both are supplied. See .env.example for the
complete environment-file equivalent.
| Variable | Required | Description |
|---|---|---|
CONFIG_FILE | No | Path to the config YAML file; default: /etc/backupctl/config.yaml. |
DATABASE_URL | Yes* | Source PostgreSQL URL. Must include sslmode=require, verify-ca, or verify-full. |
S3_ENDPOINT | Yes | HTTP(S) endpoint for S3 or an S3-compatible provider. |
S3_REGION | No | S3 region; default: auto. |
S3_BUCKET | Yes | Private destination bucket. |
S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY | Yes | Credentials scoped to the environment prefix. |
S3_FORCE_PATH_STYLE | No | Set true for path-style providers such as s3mock. |
S3_SERVER_SIDE_ENCRYPTION | No | AES256 or aws:kms. |
BACKUP_PREFIX | Yes | Prefix for one environment, for example production or staging. |
BACKUP_SCHEDULE | No | Five-field cron schedule; default: 0 3 * * *. |
VERIFY_SCHEDULE | No | Five-field cron schedule; default: 0 4 1 * *. |
TZ | No | IANA timezone; default: UTC. |
RETENTION_DAYS | No | Informational only — apply this value in your bucket's lifecycle policy; default: 14. |
SMTP_URL, ALERT_FROM, ALERT_TO | No | Configure all three to send failure alerts. |
* DATABASE_URL is not required for restore; the target URL is supplied to
that command instead.
Backups and manifests are stored as:
<BACKUP_PREFIX>/YYYY/MM/DD/<database>_<UTC timestamp>.dump
<BACKUP_PREFIX>/YYYY/MM/DD/<database>_<UTC timestamp>.json
Verification results are stored under <BACKUP_PREFIX>/verification/.
Restore is intentionally separate from verification and replaces objects in an existing target database. It refuses to target the configured source database, and requires two explicit confirmations for non-interactive use:
docker compose run --rm -it backup restore \
--database-url 'postgresql://restore_user:[email protected]:5432/staging?sslmode=verify-full' \
--yes \
--confirm-restore staging
Omit the restore flags to be prompted interactively. The source
DATABASE_URL may be omitted for restore-only use.
production and staging.RETENTION_DAYS and abort incomplete multipart uploads after 24 hours.ListBucket (scoped to its prefix), GetObject, and
PutObject; do not grant DeleteObject.pg_dump needs,
enforce TLS, and prefer sslmode=verify-full with a trusted CA.run and verify locally before production.The image runs as a non-root backupctl user and needs writable storage only
for temporary archives and restore data. The read_only and tmpfs options
shown above are recommended for production.
Content type
Image
Digest
sha256:8318fe9fd…
Size
115.8 MB
Last updated
24 days ago
docker pull jesulonimii/postgres-s3-backup