---
title: "Deploy PostgreSQL S3 backups"
description: "A simple utility to backup Postgres databases to S3 using Bun."
category: "Automation"
url: https://railway.com/deploy/postgresql-s3-backups
---

# Deploy PostgreSQL S3 backups

A simple utility to backup Postgres databases to S3 using Bun.

**[Deploy PostgreSQL S3 backups on Railway](https://railway.com/template/postgresql-s3-backups)**

- **Creator:** Nicolas's Projects
- **Category:** Automation
- **Total deploys:** 5

## Template content

### PostgreSQL S3 Backups https://devicons.railway.app/PostgreSQL

- **Source:** ncontiero/postgres-s3-backups

## Documentation

# 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](https://docs.railway.com/cron-jobs).

## 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=true` and 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_SCHEDULE` and `RUN_ON_STARTUP` are not used in this mode.
- **Continuous service:** Keep `SINGLE_SHOT_MODE=false` and configure `BACKUP_CRON_SCHEDULE`. The standalone application remains active and schedules backups with Bun's native cron implementation. Set `RUN_ON_STARTUP=true` to back up immediately when the service starts. Overlapping executions are skipped.

#### Required configuration

When PostgreSQL is another Railway service, use a [reference variable](https://docs.railway.com/variables#reference-variables). Replace `Postgres` if the database service has a different name.

```env
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:

```env
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:

```bash
gzip --decompress --stdout backup.tar.gz \
  | pg_restore --dbname="postgresql://user:password@host:port/database"
```

For more information, see the documentation for [Railway Cron](https://docs.railway.com/cron-jobs), [Bun S3](https://bun.com/docs/runtime/s3), [`pg_dump`](https://www.postgresql.org/docs/current/app-pgdump.html), and [`pg_restore`](https://www.postgresql.org/docs/current/app-pgrestore.html).

## 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.


## Similar templates

- [N8N Main + Worker](https://railway.com/deploy/n8n-main-worker) — Deploy and Host N8N with Inactive worker.
- [Evolution API with n8n](https://railway.com/deploy/evolution-api-with-n8n) — Automate WhatsApp workflows with Evolution API, n8n, and Postgres.
- [Postgres Backup](https://railway.com/deploy/postgres-s3-backups) — Cron-based PostgreSQL backup to bucket storage

Open this page in a browser: https://railway.com/deploy/postgresql-s3-backups
