How to back up a Railway Postgres database, and keep the copy outside the project
Railway gives a Postgres service two kinds of recovery, volume backups and point-in-time recovery, and both restore into the same project. Railway's own guide says only the offsite logical dumps survive a deleted project or volume. This page is that dump: how to reach the database from inside and outside the project, the command, where the file goes, the restore and the check. The last section is how SafeGrd runs it, as a service in the project or with no service at all.
What Railway's backups cover
From Railway's Postgres backups guide, read on 2026-10-08:
| Kind | Schedule and retention | Restores to |
|---|---|---|
| Volume backup, daily | Every 24 hours, kept 6 days | The same service, as a new volume, by Deploy |
| Volume backup, weekly | Every 7 days, kept 1 month | The same service |
| Volume backup, monthly | Every 30 days, kept 3 months | The same service |
| Point-in-time recovery | WAL archived with pgBackRest to a Postgres-PITR bucket; a full base backup every week and a differential one every day, the last 4 full kept | A new sibling service, <source>-restored-... |
- Wiping the volume deletes all of its backups, and a restored volume does not carry the backups taken after that point; they stay on the old volume.
- PITR starts when you turn it on. The window begins at the first base backup after enabling, so it covers nothing before that day.
- Neither leaves Railway. No download, no copy to another provider, no file to hand over. Railway's guide: "only the offsite logical dumps survive."
1. Reach the database
A Railway database is private by default. There are two ways in, and which you use decides what the dump costs:
- From a service in the same project:
DATABASE_URL, over private networking. Railway's guide runs the backup this way and notes the dump "travels over private networking with no egress cost for the database read." - From outside: open the service's Settings, Networking, and enable the
TCP proxy. Railway fills in
DATABASE_PUBLIC_URL. Traffic over the proxy is billed as network egress, so a large database dumped nightly from outside has a bill attached. From a laptop,railway connect postgres --tunnel-onlyopens a tunnel without exposing the database.
Railway's Postgres runs its SSL-enabled image; add ?sslmode=require to a
public string. The default user is the one Railway created; make a read-only one for the
backup so a leaked credential cannot write:
2. Dump it
Railway's guide uses the same flags: custom format because it is compressed and lets
pg_restore run selectively and in parallel, --no-owner on both the
dump and the restore. The client has to be at least the server's major version; Railway
builds from the official postgres image, so check SELECT version()
and install that postgresql-client.
3. Off the platform, encrypted, on a schedule
Encrypt the dump on the way out and write it to a bucket in another account, where a deleted project cannot reach it:
Two places to run it:
- A Railway cron service. A service in the project with a Cron Schedule in its settings runs the command on the private network, so the read costs no egress, then exits. Schedules are UTC, at least 5 minutes apart, and a run still going when the next is due makes Railway skip the next, so a hung dump blocks the ones after it. The upload to the bucket is egress either way.
- A scheduled GitHub Actions workflow, through the TCP proxy.
The Supabase page has a 10-line workflow; put
DATABASE_PUBLIC_URLin the secret.
set -o pipefail makes a failed dump fail the job instead of uploading a
truncated file. The bucket policy, a key that cannot delete and the lock on the bucket are
in the S3 guide and
the Object Lock guide.
4. Restore, and check it
Railway's guide has the right habit: restore into a scratch database named
restore_drill, check the row counts and the newest rows, record how long it
took and how old the dump was, then drop it. Those two numbers are your real recovery time
and recovery point.
To put a project back after it is gone: a new Postgres service on Railway, or any
PostgreSQL of the same or a newer major version, an empty database, and the same
pg_restore. How to test that a
backup restores has the exact per-table count inside one snapshot, for when an estimate
is not enough.
How SafeGrd does it
SafeGrd runs the daemon as a service in the project, where it reads the database over
private networking with no egress for the read, or takes the backup from a GitHub Actions
job through the TCP proxy. Its scheduled drill is the restore_drill Railway's
guide asks you to run, with the row counts checked and the result signed. SafeGrd does the same job on every platform; the PostgreSQL page has what it dumps, how it encrypts, and what a drill checks.
Run your first Fire Drill Read the install guide
As a service in the project
- Add a service from the image
ghcr.io/safegrd/cli, with a volume mounted at/home/safegrd/.safegrdso the host's key and config survive a redeploy, and the database'sDATABASE_URLas a variable. - Open a shell on it once (the Railway CLI's
railway ssh) and runsafegrd enroll, which signs the host in and registers it. Then add the database as a surface in the SafeGrd console, with the read-only role. - The image's default command is
safegrd daemon run, which backs up and drills on the surface's schedule. A sandbox drill runs on the PostgreSQL server the image carries, so the restore test never touches the live database.
With no service
The GitHub Actions setup in the Supabase
guide works for Railway with DATABASE_PUBLIC_URL in the secret.
Or, under Surfaces in the console, Back up on SafeGrd takes the public string; a
machine SafeGrd starts for each backup dumps and encrypts it, then is destroyed, and the
string is sealed and given only to that machine (how).
Both read through the TCP proxy, so Railway bills the read as egress; a backup that
uploads only what changed still reads the whole database.
Questions
Should I turn off Railway's backups?
No. Volume backups and PITR are the fast undo inside the project, and they cost little. The dump outside the project is for when the project, the volume or the account is the thing that was lost.
Does the daemon work on an HA cluster?
Yes. It reads from whichever node the connection string resolves to; point it at a replica to keep the read off the primary.
Related
- How to back up a PostgreSQL database: pg_dump's formats and flags, restore and the check
- How to back up PostgreSQL to S3: the full script, the bucket policy and cron
- Render backup: the same job on Render