Backups and recovery are the boring half of database operations until they’re not. Render takes care of the daily snapshot rhythm and offers point-in-time recovery on paid plans - but the moment you need a backup is the moment you discover whether you understood the rules.
This step is the playbook: what’s automatic, what you need to do yourself, and the deletion rule everyone wishes they’d known sooner.
What Render does for you
flowchart LR
pg[("Postgres instance")]
pitr["Continuous PITR<br/>(paid plans)"]
logical["On-demand logical backups<br/>(.dir.tar.gz, 7-day retention)"]
pg --> pitr
pg --> logical
| Mechanism | What it covers | Retention |
|---|---|---|
| Point-in-time recovery (PITR) | Continuously back up paid databases. Restore to any moment within the recovery window | 3 days (Hobby workspace) or 7 days (Pro+ workspace) |
| On-demand logical backups | Compressed .dir.tar.gz exports you trigger from the Render Dashboard. Download for long-term retention or restore elsewhere | 7 days regardless of plan |
The combination of continuous PITR within the recovery window plus on-demand exports covers most of the “oh no” scenarios - accidental deletes, bad migrations, an UPDATE without a WHERE clause.
Recovery windows by workspace plan
Hobby workspace Pro or higher workspace ─────────────── ──────────────────────── Past 3 days Past 7 daysA few rules worth memorizing:
- PITR is not available on the Free instance type. Upgrade the instance to enable it.
- Upgrading the workspace plan does not retroactively backfill the recovery window. If you upgrade Hobby → Pro today, your recovery window extends to 7 days going forward - yesterday’s data still falls off after 3 days from creation.
- You can’t restore to within the last 10 minutes. Render needs a small lead time to consolidate backup state.
- Recovery instances are billed. They’re full Postgres instances until you delete them - clean up after a successful cutover.
Performing a recovery
PITR always creates a new instance, never overwrites the original. That makes the flow safer than it sounds: spin up the recovery instance, validate it, then cut over.
- Open the database's Recovery page in the Render Dashboard In the Render Dashboard, select your database from the service list and open
Recovery. - Scroll to Point-in-Time Recovery and click Restore Database A modal appears.
- Provide a name for the recovery instance A new database resource will be created with this name.
- Pick the date and time to restore to Within the recovery window. Can’t be within the last 10 minutes.
- Choose whether to copy existing settings Yes copies instance type, Datadog API key, and project. No lets you change them. Either way, the recovery instance copies the IP allowlist from the original.
- Click Start Recovery Status advances from
Recovery In Progress→Creating→Available. - Validate the recovered data Connect to the new instance with the PSQL Command on its Info page and confirm what you expect to see is there.
- Update your services to point at the recovery instance Either edit
fromDatabasereferences in your Blueprint, or update an env group (one place to changeDATABASE_URLfor many services). Resync. - Delete or suspend the original instance Once you’ve verified all systems are connecting to the recovery instance.
Logical backups: the on-demand kind
PITR is great for “restore to a point in time” inside Render. For portable backups - files you download, archive, hand to another database service, or restore on your laptop - you have two paths:
- Render-generated logical backups triggered from the Render Dashboard. Render produces a compressed directory archive (
.dir.tar.gz) and retains it for 7 days. - Self-managed
pg_dumpthat you run from your laptop or a Render cron job. You control the format, retention, and storage location.
Render-generated logical backups
From the database’s Recovery page, click Create export. Render produces a .dir.tar.gz file you can download from the Render Dashboard. Each export’s filename encodes its timestamp (e.g. 2026-05-20T19_21Z.dir.tar.gz).
A few constraints to know:
- Only one export at a time per database - the next “Create export” is disabled while one is in progress.
- Not available on the Free instance type. Upgrade the instance, or run
pg_dumpfrom your laptop instead. - Render retains the export file for 7 days regardless of workspace plan. Download what you want to keep longer.
Restoring a Render export
Render’s exports are in directory format, restored with pg_restore --format=directory:
# Extract the exporttar -zxvf 2026-05-20T19_21Z.dir.tar.gz
# Restore into a target database (note --clean and --if-exists drop matching objects first)pg_restore \ --dbname="$EXTERNAL_DATABASE_URL" \ --format=directory \ --clean --if-exists --no-owner --no-privileges \ --verbose \ 2026-05-20T19_21Z/my_render_database_nameSelf-managed pg_dump
When you want full control - different format, different retention, your own off-Render storage - run pg_dump yourself:
pg_dump "postgres://user:pass@host/db?sslmode=require" > backup.sqlpg_dump -Fc "postgres://user:pass@host/db?sslmode=require" -f backup.dumpUse plain SQL when:
- The database is small (under ~2 GB).
- You want a human-readable file you can
grepand edit. - You’ll restore with
psql backup.sql.
Use custom format (-Fc) when:
- The database is bigger than a few GB.
- You want compression (significant space savings).
- You’ll restore with
pg_restore, optionally in parallel (pg_restore -j 4 --format=custom).
For most production databases, -Fc is the right default for self-managed dumps. Plain SQL is for dev and ad-hoc work.
Where to run pg_dump from
Three options, in increasing order of robustness:
The simplest path for ad-hoc work. Use the external URL with ?sslmode=require:
pg_dump -Fc "postgres://user:pass@external-host/db?sslmode=require" -f backup.dumpMake sure your laptop’s IP is in the database’s allowlist (Dashboard → Access Control). Don’t cat the URL - keep it in an env var so it doesn’t end up in shell history.
For scheduled backups, run a cron job that uses the internal URL and uploads the dump to S3 or another durable store:
services: - type: cron name: pg-dump-nightly runtime: docker schedule: "0 4 * * *" image: url: ghcr.io/yourorg/pg-dump-uploader:latest envVars: - key: DATABASE_URL fromDatabase: name: app-db property: connectionString - key: AWS_S3_BUCKET value: my-postgres-backups - key: AWS_ACCESS_KEY_ID sync: false - key: AWS_SECRET_ACCESS_KEY sync: falseThe image is a small wrapper around pg_dump -Fc and the AWS CLI. Build once, run nightly. This is the right pattern for any database whose data matters more than Render’s retention window.
render psql (the CLI’s shell access) is fine for small ad-hoc dumps:
render psql <database-id> -- -c '\copy (SELECT * FROM users) TO STDOUT' > users.csvFor databases under ~2 GB, this works. For larger ones, validate against shell timeout limits before relying on it for production backups.
Restoring a self-managed pg_dump
Match the command to the format you wrote with:
psql "postgres://user:pass@host/db?sslmode=require" < backup.sqlpg_restore --format=custom -d "postgres://user:pass@host/db?sslmode=require" backup.dumpFor large -Fc dumps, parallel restore is dramatically faster:
pg_restore -j 4 --format=custom -d "postgres://..." backup.dump-j 4 runs four restore workers in parallel. Pick the number based on your target instance’s CPU count; on a 4-core plan, 4 is a good default. (For Render’s .dir.tar.gz exports, use --format=directory instead - covered above.)
Test your restores
The single most useful habit you can build: periodically restore a recent dump to a non-production database and confirm it works. A backup you’ve never restored is hope, not a backup.
Once a quarter is enough for most teams. Make it part of the on-call rotation: rotate who runs the restore drill, write down what was different from last time, fix the surprises before they’re real.
The deletion rule that loses data
flowchart LR alive["Render Postgres<br/>(snapshots + PITR)"] delete["Delete instance"] gone["No more snapshots,<br/>no more PITR - everything is gone"] alive --> delete --> gone
This trips up teams in two scenarios:
- Cleanup after a migration. You moved data to a new database, the new one is working, you delete the old one - and a week later you realize you needed a row from the old database. The snapshots aren’t there to fall back to.
- Tearing down a staging environment. The data was “just staging” so it didn’t seem important; six months later, “just staging” turns out to have had a useful test fixture.
The rule of thumb: pg_dump and store the dump before any deletion, even if you’re sure you don’t need it. The cost is a few minutes; the upside is a get-out-of-jail card if you’re wrong.
What about cross-region disaster recovery?
Render doesn’t ship a built-in cross-region replication product. The options:
pg_dumpto a different-region object store (e.g. S3 in us-east-1 even if your database is in oregon). Your backup survives a region outage even if the live database doesn’t.- Self-managed logical replication to a Postgres instance in another region. More moving parts; you operate the replication yourself.
- Periodic restore to a warm secondary in another region. A cron job that nightly restores the latest backup to a database in a different region - expensive but real.
Most teams don’t go beyond the first option. It’s enough to recover from a regional event with a few hours of data loss, which is the right trade-off for most apps below the “global SaaS” tier.
What you learned
- PITR is continuous and automatic on paid Postgres. Recovery window is 3 days on Hobby workspaces, 7 days on Pro+. Not available on Free instances
- PITR always creates a *new* instance - validate it before cutting consumers over. The recovery instance is billable until you delete the original
- On-demand logical backups produce `.dir.tar.gz` files, restored with `pg_restore --format=directory`. Retained 7 days on Render regardless of plan
- Self-managed `pg_dump -Fc` is the portable backup; pair with `pg_restore --format=custom` for restore. Run nightly from a cron job and upload to S3/R2 for off-Render durability
- Deleting a Postgres instance deletes its PITR state, exports, and snapshots - all of it, permanently. Download or `pg_dump` everything important before deletion
- Test your restores quarterly - a backup you've never restored is hope, not a backup