How to back up a Render PostgreSQL database
Render keeps 3 to 7 days of point-in-time recovery and 7-day exports for paid Postgres databases, and nothing once a database is deleted. To keep your own copies, run pg_dump at the database's major version or newer, either from your own server over the external URL with its IP allowed, or from a Render cron job over the internal URL, and send the dump to storage outside Render.
What Render backs up for you
Render backs up databases on paid compute plans continuously, for point-in-time recovery (PITR), and lets you create logical exports from the database's Recovery page. Databases on the Free compute plan get neither.
| Point-in-time recovery | Logical exports | |
|---|---|---|
| How far back | The past 3 days on a Hobby workspace, 7 days on Pro or higher | Each export is kept 7 days after it is created, on every workspace plan |
| How you start it | Restore Database, then a time at least 10 minutes in the past | Create export, one at a time per database |
| What you get | A new database at that moment; the original keeps running | A .dir.tar.gz file holding a pg_dump directory-format archive, to download |
| Free compute plan | Not available | Not available |
Upgrading a Hobby workspace to Pro doesn't backfill the window; the 3 days grow to 7 going forward. The recovery database copies the original's IP allow list. You check it, point your services at it, then delete or suspend the original.
What happens when the database or workspace is deleted
Render's documentation is direct: it does not retain backups or snapshots of a deleted database instance. The recovery history and any exports you haven't downloaded go with it. The database also goes when something around it does:
- Deleting a project deletes all of its environments and services. Deleting an environment deletes its services.
- A Free database expires 30 days after creation. You then have 14 days to upgrade it, after which Render deletes it and all its data.
- Everything you create belongs to a workspace, and Render's docs describe no backup kept outside it. Treat losing the workspace, or access to it, as losing the database.
A protected environment lets only workspace Admins delete its resources or change a database's allowed IPs. That stops a teammate's mistake, not an Admin's, and not someone who has taken over an Admin account.
Why keep your own dumps
Render's backups are the fastest way back to a recent moment. When data goes missing, try PITR first: it almost always reaches newer data than any dump. But they share the database's fate and the workspace's logins, and reach back at most 7 days. PITR restores only into Render, and exports expire unless you download them.
A nightly pg_dump in a bucket you control covers the rest: deletion, a lost account, a mistake found on day 10, and a move to another host. That is the 3-2-1 rule applied to a database you don't run yourself; managed database backups makes the same case for DigitalOcean and AWS RDS.
Match the pg_dump version to the database
pg_dump refuses to dump a server whose major version is newer than its own. New Render databases default to PostgreSQL 18, and versions 13 to 18 are fully supported. Copy the external URL from the database's Connect menu and compare:
psql "<EXTERNAL_DATABASE_URL>" -Atc "show server_version"pg_dump --versionOn Ubuntu or Debian, install the matching client, such as postgresql-client-18, from the PostgreSQL apt repository; the managed database guide shows the setup. Render's native runtimes include postgresql-client packages, but the docs don't tie them to your database's version, so the cron job below pins its own.
Dump from your own server over the external URL
By default a Render database accepts external connections from any IP address with valid credentials. Narrow that to your backup server: on the database's Info page, under Networking, replace the 0.0.0.0/0 rule with the server's address as a /32. Rules are IPv4 CIDR ranges only. Render services in the same region keep connecting over the internal URL either way. The Render CLI does the same; this replaces the whole list:
render pg update my-database --ip-allow-list "cidr=203.0.113.42/32,description=backup-server"External connections are always encrypted. Add sslmode=require so libpq refuses to fall back to plain text. The URL contains the password, so keep it in a file only root can read:
DATABASE_URL='postgresql://USER:PASSWORD@EXTERNAL_HOST:5432/DATABASE?sslmode=require'sudo chmod 600 /etc/render-backup.envThis script dumps to a temporary name, reads the whole archive back so a truncated file fails here rather than during a restore, then keeps 14 days:
#!/usr/bin/env bash
set -euo pipefail
source /etc/render-backup.env
BACKUP_DIR="/var/backups/render"
KEEP_DAYS=14
OUT="$BACKUP_DIR/mydb-$(date -u +%Y-%m-%dT%H%MZ).dump"
mkdir -p "$BACKUP_DIR"
trap 'rm -f "$OUT.partial"' EXIT
pg_dump --dbname="$DATABASE_URL" --format=custom --no-password --file="$OUT.partial"
pg_restore --file=/dev/null "$OUT.partial"
mv "$OUT.partial" "$OUT"
find "$BACKUP_DIR" -name 'mydb-*.dump' -type f -mtime +"$KEEP_DAYS" -delete--format=customwrites a compressed archive that pg_restore can restore in full or in part.--no-passwordnever prompts, so a cron run fails at once instead of hanging.- Render's own example adds
-n public. Leave it out unlesspublicis your only schema: with-n, pg_dump skips other schemas, large objects and anything outside the schema it depends on. - Use the direct URL, not a connection pool URL (port
6432). Render's backup guide says not to back up through PgBouncer, and its pools use transaction-level pooling.
15 3 * * * root /usr/local/bin/render-pg-backup.sh >> /var/log/render-pg-backup.log 2>&1Make the script executable with chmod 755. The dumps are now off Render but on one server, so copy them to a bucket with rclone or the AWS CLI (bucket setup).
Dump from inside Render with a cron job
To keep the database closed to the internet, run the dump on Render. Cron jobs can reach a database in the same region and workspace over the private network, using its internal URL, and inbound IP rules don't apply there. Two limits shape the job: cron jobs can't use a persistent disk, so stream the dump to object storage, and Render stops any run after 12 hours.
Put two files in a Git repository. Alpine 3.23 packages the PostgreSQL 16, 17 and 18 clients, each in /usr/libexec/postgresql<version>; change both 18s to your database's version. For 13 to 15, Render's example repository lists an older Alpine release that has the client:
FROM alpine:3.23
RUN apk add --no-cache bash postgresql18-client aws-cli
ENV PATH="/usr/libexec/postgresql18:$PATH"
COPY backup.sh /usr/local/bin/backup.sh
RUN chmod 755 /usr/local/bin/backup.sh
ENTRYPOINT ["/usr/local/bin/backup.sh"]#!/usr/bin/env bash
set -euo pipefail
KEY="render/mydb-$(date -u +%Y-%m-%dT%H%MZ).dump"
pg_dump --dbname="$DATABASE_URL" --format=custom | aws s3 cp - "s3://$S3_BUCKET/$KEY.partial"
aws s3 mv "s3://$S3_BUCKET/$KEY.partial" "s3://$S3_BUCKET/$KEY"
echo "Uploaded s3://$S3_BUCKET/$KEY"aws s3 cp -uploads from standard input. If pg_dump dies, the upload can still finish with whatever arrived, so the file is renamed only after both succeed. A leftover.partialobject is a failed run; a lifecycle rule can expire them.- Above 50 GB, add
--expected-sizewith the approximate byte count toaws s3 cp. - For storage other than AWS, set
AWS_ENDPOINT_URLto the provider's S3 endpoint. - TLS on internal connections is optional and uses self-signed certificates. Add
?sslmode=requireto the URL to insist on it;verify-caandverify-fulldon't work there.
Create the cron job from the repository in the Render Dashboard. Pick the database's region and the Docker runtime, a schedule such as 0 3 * * * (schedules are UTC), and the smallest plan. Set DATABASE_URL to the internal URL from the database's Connect menu, plus S3_BUCKET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_DEFAULT_REGION. The image's ENTRYPOINT runs the script. Click Trigger Run on the Runs page, then check the bucket.
Cron jobs are billed per second of running time, with a minimum of $1 a month per cron job service (as of October 2026). Render's own walkthrough builds a similar job that uploads a gzipped plain-SQL dump.
Restore into a new Render Postgres database
Create a database on the same major version or newer, in the region your services use, and restore into it while it is still empty. Run pg_restore from a machine with a matching client, using the new database's external URL:
pg_restore --dbname="<NEW_EXTERNAL_DATABASE_URL>" --no-owner --no-privileges mydb-2026-10-03T0315Z.dump--no-ownermakes the restoring user own every object, since the original owner role may not exist.--no-privilegesskipsGRANTandREVOKEfor roles the new database doesn't have.- pg_restore carries on past errors and prints a count at the end. Read them before you switch over.
To restore one of Render's own exports, extract it first. It unpacks to a folder named with the export time, holding a directory-format dump named after your database:
tar -zxvf 2025-02-03T19_21Z.dir.tar.gzpg_restore --dbname="<NEW_EXTERNAL_DATABASE_URL>" --no-owner --no-privileges --jobs=4 --format=directory 2025-02-03T19:21Z/<database_name>--jobs=4 loads data and builds indexes in up to four sessions at once, which directory archives allow. Then update DATABASE_URL on your services (an environment group changes several at once) and keep the old database until everything checks out.
Test the restore
A dump you have never restored is a hope. Once a month, restore the latest one into a throwaway database and compare a few important tables with production:
psql "<NEW_EXTERNAL_DATABASE_URL>" -Atc "SELECT count(*) FROM orders"Time it as well; that is your real recovery time. If the restored database fits in 1 GB, a Free Render database makes a fine target (one per workspace). Otherwise create a paid one and delete it afterwards, or restore into a local container as in testing a restore.
Common errors
| Error | Fix |
|---|---|
aborting because of server version mismatch | pg_dump is older than the database. Install the matching client, or change the version in the Dockerfile. |
No SNI information found | The client connected to an IP address. Use the full host name from the external URL. |
pg_dump can't connect from your server, though nc says the port is open | Its IPv4 address isn't in the database's inbound IP rules. Render checks them after the TCP handshake, so port checks pass anyway. |
could not translate host name ... to address | You used the internal URL outside Render. It works only from Render services in the same region and workspace. |
| No Create export button or recovery options | The database is on the Free compute plan, which has no backups. Upgrade it, or use pg_dump. |
role "..." does not exist during a restore | Add --no-owner --no-privileges. |
unsupported version (...) in file header | pg_restore is older than the pg_dump that wrote the archive. Use the same version or newer. |
Frequently asked questions
- Does Render back up PostgreSQL automatically?
- On paid compute plans, yes: continuous backups for point-in-time recovery over the past 3 days on a Hobby workspace or 7 days on Pro and higher. Free databases have no backups.
- How long does Render keep Postgres exports?
- Seven days after each export is created, on every workspace plan. Download the
.dir.tar.gzfile to keep it longer. - Are backups kept if I delete a Render database?
- No. Render does not retain backups or snapshots of a deleted database. Download an export or take a pg_dump first.
- Can I use the internal database URL from my own server?
- No. It works only for Render services in the same region and workspace. Use the external URL and allow your server's IPv4 address.
How this was checked
Commands, limits and prices were checked against these official pages, on October 3, 2026:
- Render: Render Postgres Recovery and Backups
- Render: Create and Connect to Render Postgres
- Render: Inbound IP Rules
- Render: Cron Jobs
- Render: Private Network
- Render: Back Up Render Postgres to Amazon S3
- Render examples: postgres-s3-backups
- Render: Deploy for Free (Free Postgres limits)
- Render: Projects and Environments
- Render: Workspaces, Members, and Roles
- Render: Connection Pooling for Render Postgres
- Render: Upgrading Your Render Postgres Version
- Render: Native Runtimes
- PostgreSQL documentation: pg_dump
- PostgreSQL documentation: pg_restore
- Alpine Linux packages: postgresql18-client (v3.23)
- AWS CLI Command Reference: s3 cp
- AWS CLI User Guide: Environment variables