Deploy PostgreSQL S3 backups
A simple utility to backup Postgres databases to S3 using Bun.
PostgreSQL S3 Backups
Just deployed
Deploy and Host PostgreSQL S3 backups on Railway
PostgreSQL S3 backups is a lightweight service that creates compressed PostgreSQL backups and uploads them to AWS S3, Cloudflare R2, MinIO, or another S3-compatible storage provider. Run it continuously with Bun's native scheduler or once per deployment with Railway Cron.
About Hosting PostgreSQL S3 backups
Hosting PostgreSQL S3 backups involves connecting the service to a PostgreSQL database and an S3-compatible bucket through Railway variables. For each execution, the service streams pg_dump into a Gzip-compressed tar archive, validates it, uploads it, and removes the temporary local file. It supports automatic multipart uploads, configurable retention, custom pg_dump options, and PostgreSQL client versions 14 through 18. You can use Railway Cron to start one single-shot deployment per schedule or keep the service running with Bun's native scheduler. No persistent volume or separate Bun runtime installation is required in the final Docker image.
Common Use Cases
- Schedule PostgreSQL backups to off-site S3-compatible storage.
- Apply a configurable retention period to control storage usage.
- Back up Railway PostgreSQL services without maintaining a separate backup server.
Dependencies for PostgreSQL S3 backups Hosting
- A reachable PostgreSQL database and its
DATABASE_URL. - An AWS S3 or S3-compatible bucket with credentials that can upload objects. Listing and deleting objects are also required when retention is enabled.
Implementation Details
Deployment modes
- Railway Cron (recommended): Set
SINGLE_SHOT_MODE=trueand configure a Cron Schedule in the Railway service settings. Railway starts the service for each execution, and the process exits when the backup finishes. Railway schedules use UTC, support a minimum interval of five minutes, and skip an execution if the previous one is still active.BACKUP_CRON_SCHEDULEandRUN_ON_STARTUPare not used in this mode. - Continuous service: Keep
SINGLE_SHOT_MODE=falseand configureBACKUP_CRON_SCHEDULE. The standalone application remains active and schedules backups with Bun's native cron implementation. SetRUN_ON_STARTUP=trueto back up immediately when the service starts. Overlapping executions are skipped.
Required configuration
When PostgreSQL is another Railway service, use a reference variable. Replace Postgres if the database service has a different name.
DATABASE_URL=${{Postgres.DATABASE_URL}}
AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
S3_BUCKET=your-bucket-name
S3_REGION=us-east-1
Set S3_ENDPOINT when using a provider other than AWS:
S3_ENDPOINT=https://your-s3-compatible-endpoint
Environment variables
| Variable | Description | Default |
|---|---|---|
AWS_ACCESS_KEY_ID | S3 access key ID. | Required |
AWS_SECRET_ACCESS_KEY | S3 secret access key. | Required |
S3_BUCKET | Destination bucket name. | Required |
S3_REGION | Destination bucket region. | Required |
S3_ENDPOINT | Custom endpoint for an S3-compatible provider. | |
DATABASE_URL | PostgreSQL connection URL. | Required |
BACKUP_CRON_SCHEDULE | Five-field cron schedule used in continuous mode. | 0 0 * * * |
BACKUP_FILE_PREFIX | Prefix used in backup object names. | backup |
BACKUP_RETENTION_DAYS | Delete matching backups older than this number of days. | |
BUCKET_SUBFOLDER | Optional bucket path in which backups are stored. | |
BACKUP_OPTIONS | Additional space-separated arguments passed to pg_dump. | |
RUN_ON_STARTUP | Run a backup when a continuous service starts. Accepts true, false, 1, 0. | false |
SINGLE_SHOT_MODE | Run one backup and exit. Accepts true, false, 1, 0. | false |
PG_VERSION is a Docker build argument rather than a runtime variable. It accepts versions 14 through 18, defaults to 18, and should preferably match the database server's major version.
Backup behavior
Backups are named <prefix>-<ISO timestamp>.tar.gz. The service creates a PostgreSQL tar archive, compresses it with Gzip, validates the expected archive entries, and uploads it using Bun's S3 client. Bun automatically uses multipart uploads when appropriate. A dump, validation, or upload failure makes single-shot mode exit with status 1; continuous mode logs the error and keeps future executions scheduled. Retention and temporary-file cleanup errors are reported separately and do not invalidate an already uploaded backup.
To restore a downloaded backup, decompress it and pipe the archive into a compatible pg_restore client:
gzip --decompress --stdout backup.tar.gz \
| pg_restore --dbname="postgresql://user:password@host:port/database"
For more information, see the documentation for Railway Cron, Bun S3, pg_dump, and pg_restore.
Why Deploy PostgreSQL S3 backups on Railway?
Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.
By deploying PostgreSQL S3 backups on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.
Template Content
PostgreSQL S3 Backups
ncontiero/postgres-s3-backupsS3_BUCKET
The name of your S3 bucket.
S3_REGION
The region of your S3 bucket.
S3_ENDPOINT
The endpoint for your S3-compatible service (optional).
DATABASE_URL
The connection URL for your PostgreSQL database.
AWS_ACCESS_KEY_ID
Your S3 access key ID.
AWS_SECRET_ACCESS_KEY
Your S3 secret access key.