How to back up a Render Postgres database, and keep a copy past the recovery window
Render backs up paid Postgres databases continuously and can recover one to a point in the last 3 or 7 days, depending on the workspace. A Free database has no recovery at all, and no plan keeps a copy you hold for longer than a week. This page is the dump that does: the external URL, the role, the command, where the file goes, the restore and the check. The last section is how SafeGrd runs it with no server of your own.
What Render's recovery covers
From Render's Postgres backups page, read on 2026-10-08:
| Workspace | Point-in-time recovery | Logical backups |
|---|---|---|
| Free database | None. "Render does not provide recovery capabilities for databases on the Free compute plan." | None; run pg_dump yourself |
| Hobby | The past 3 days | A .dir.tar.gz you can download, kept 7 days |
| Pro and above | The past 7 days | The same, kept 7 days |
- Recovery makes a new instance at the chosen time; you check it and point your services at it. You cannot restore to a time within ten minutes of now.
- Upgrading from Hobby does not backfill the window: 7 days of coverage starts the day you upgrade.
- A logical backup you download is kept 7 days on Render's side. Keeping one for a year means putting it somewhere else.
1. The external URL, and the allow list
A Render database has an internal URL, for services in the same region, and an external one for everything else. A backup from outside Render uses the external one:
- TLS is always on for external connections, and Render rejects
sslmode=disable. Setsslmode=require. - The IP allow list defaults to
0.0.0.0/0, open to every address. If you have narrowed it, add the address the backup runs from.render pg update --ip-allow-listreplaces the whole list, so include the blocks you already have. - The internal URL is faster and free of the public internet, and only a Render service in the same region can use it. A Render cron job or background worker from the same workspace qualifies.
Make a role for the backup that can read and cannot write:
2. Dump it
The client has to be at least the server's major version, which Render shows on the
database's page. --no-owner keeps Render's generated role name out of the
dump, so it restores anywhere.
3. Off Render, encrypted, on a schedule
- A Render cron job runs the command on a schedule you set as a cron expression, in UTC, from a repository or a Docker image, and reaches the database over the internal URL when it is in the same region. Render stops a run after 12 hours and runs one at a time. It is billed per second with a minimum of $1 a month per job, and it cannot mount a persistent disk, which is fine for a dump that streams to a bucket.
- A scheduled GitHub Actions workflow, over the external URL. The Supabase page has a 10-line workflow; put the external string in 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
Create an empty database, on a new Render instance or any PostgreSQL of the same or a newer major version, and restore over its external URL. Stop at the first error:
Then compare row counts per table with the source, as
How to test that a backup restores
shows. Render's own downloadable backup is a different
artefact, a .dir.tar.gz of a directory-format dump, and pg_restore
takes the unpacked directory the same way.
How SafeGrd does it
SafeGrd takes the backup from a scheduled GitHub Actions job over the external URL, or on a machine it starts for each backup from the connection string, encrypts it before upload, and keeps a copy for as long as the retention you set, not Render's 7 days. 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 The GitHub Actions setup
- uses: safegrd/backup-action@v0
with:
config: ${{ secrets.SAFEGRD_CONFIG }}
database-url: ${{ secrets.DATABASE_URL }} # the external URL
The setup is the Supabase guide's with
Render's external string in DATABASE_URL. With no
workflow at all, Back up on SafeGrd under Surfaces in the console takes the same
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). Where SafeGrd's machines connect from fixed
addresses, the console shows them, for an allow list that is not open.
Questions
Does this replace Render's point-in-time recovery?
No. Recovery takes the database to any minute in its window and makes a new instance in one click; a dump restores to the moment it was taken. Keep recovery for the quick undo, and the dump for anything older than a week or after the workspace is gone.
Can the daemon run on Render?
As a background worker from the ghcr.io/safegrd/cli image with a persistent
disk for its key and config, reading the internal URL. A cron job cannot hold the disk.
The workflow keeps nothing running on Render at all.
Related
- How to back up a PostgreSQL database: pg_dump's formats and flags, restore and the check
- Railway backup: the same job on Railway
- pg_dump command builder: pick the flags and get the matching restore