DEV Community

Peon Sh
Peon Sh

Posted on Originally published at peon.sh

Automated Postgres Backups to S3: Set It and Forget It

A sound pg_dump method that includes retention policies, S3 compatible storage, and restore tests for self-managed Postgres.

The two failure modes you are insuring against
There are two types of disasters that database backups help guard against. The first type is related to infrastructure failures such as disk failures, server deletion, and problems with the provider’s account. The second and far more likely type of disaster is data corruption due to data errors such as a flawed migration, a bug in the bulk update script, or a DELETE without a WHERE clause. The first problem requires an off-site backup, while the second one requires history since the damage is discovered after several days.

The baseline: scheduled logical dumps
If your database is around 50 GB, pg_dump is easy to use, highly portable, and has been tested for years. Dump in a custom format to enable compression and select restore:

-Fc (custom format) compresses and lets you restore single tables later

Dump from a scheduled job on the server, not your laptop

Above ~50 GB, or with strict recovery-point requirements, graduate to WAL archiving (pgBackRest or wal-g) for point-in-time recovery

inside or against the container

pg_dump -U app -Fc appdb > backup-$(date +%F).dump
# verify the archive is readable (cheap, catches corruption)
pg_restore --list backup-2026-05-09.dump > /dev/null && echo OK
Ship dumps off the server
Having a backup stored in the same drive as the database will defend absolutely nothing important. Ship all your dumps to a compatible object store that resides in a separate failure zone: AWS S3, Cloudflare R2, Backblaze B2, or Hetzner Object Storage. When it comes to object stores for backing up volumes, R2 and B2 reign supreme by offering the most competitive pricing of about half a cent per GB-month with zero-cost or negligible-cost data egress upon restoration.

Use write-only permissions for uploading credentials on the object storage whenever possible and enable bucket versioning as a secondary measure.

Retention that follows how mistakes are detected
The standard grandfather-father-son rotation process is there because mistakes in the data are detected later in time. Maintain 7 daily, 4 weekly and 6 monthly dumps; this ensures that “restore yesterday” and “what was this table in February?” are satisfied and storage is limited to 17 dumps per database.

Peon’s managed databases do this via configuration settings; configure the schedule, retention numbers and S3 location through the dashboard, and Peon handles the dumping, uploading, pruning and integrity checking. Restores can be done in one click, either into the production database or the scratch database.

Schedule restoration testing on a schedule
Untested backups are hopes, not plans, and a failure in the backup system is silent: expired credentials, full disks, or a schema update that stops the dump process. Test the restoration of the latest dump to a test database, and count the rows against the production system, and time it. That time is your true recovery time; better to learn it during a drill than a disaster.

createdb restore_test
pg_restore -d restore_test --no-owner backup-2026-05-09.dump
psql restore_test -c "SELECT count(*) FROM users;"
The checklist
Nightly pg_dump -Fc, automated on the server

Uploaded to S3-compatible storage in a different provider or region

7/4/6 retention pruning, automated

Bucket versioning on; upload credentials cannot delete

Quarterly restore drill with a timed, written procedure

Alert if a backup fails or is smaller than expected; silence must be suspicious

Deploy it on your own server Peon

Top comments (0)