Heroku Postgres has its own logical backup tool, PG Backups (heroku pg:backups), and higher tiers add continuous protection for disaster recovery. Those backups are stored by Heroku and kept for a limited time depending on your plan. To keep copies on your own machine, either download Heroku's backups or run pg_dump yourself.
Option 1: Download Heroku's own backups
With the Heroku CLI installed and signed in:
heroku pg:backups:capture --app my-app
heroku pg:backups:download --app my-app --output "D:\Backups\my-app-$(Get-Date -Format yyyyMMdd-HHmm).dump"You can also schedule captures inside Heroku with heroku pg:backups:schedule DATABASE_URL --at '02:00 America/New_York' --app my-app, and then only download them. The downloaded file is a normal pg_dump custom-format archive that pg_restore reads anywhere. Scheduling the download on Windows needs the Heroku CLI signed in for the account that runs the task.
Option 2: Run pg_dump yourself
Get the current credentials:
heroku pg:credentials:url DATABASE_URL --app my-appand back up with SSL:
$env:PGSSLMODE = 'require'
& 'C:\Program Files\PostgreSQL\18\bin\pg_dump.exe' `
-h ec2-12-34-56-78.compute-1.amazonaws.com -p 5432 -U u1abc2def3 `
-F c -f "D:\Backups\my-app-$(Get-Date -Format yyyyMMdd-HHmm).dump" d4ghi5jkl6Use client tools at least as new as your Heroku Postgres version (heroku pg:info shows it).
The catch: Heroku rotates credentials
Heroku changes database credentials during some maintenance. On Essential plans this happens with every maintenance event, and on all plans during certain security patching. Your Heroku app picks up the new DATABASE_URL automatically, but an external backup job doesn't. One night the backup fails with password authentication failed, or the host has changed.
Two ways to cope:
- Fetch the URL at backup time. Have the script run
heroku pg:credentials:url DATABASE_URL --app my-app(or read the config var via the Heroku Platform API) before each dump, so it always uses current credentials. - Watch for failures. Check the backup log, and update the stored credentials when Heroku emails about maintenance.
Option 1 sidesteps this, because the CLI always talks to the current database.
Scheduling either option
Use the script and Task Scheduler setup from the main Windows guide. For option 1, replace the pg_dump call with the two heroku pg:backups commands and keep the logging and retention parts. For option 2, use the values from pg:credentials:url with $env:PGSSLMODE = 'require'.
Restoring
& "$bin\createdb.exe" -h localhost -U postgres my_app_copy
& "$bin\pg_restore.exe" -h localhost -U postgres -d my_app_copy --no-owner --no-privileges D:\Backups\my-app-20261014-0200.dumpTo restore into Heroku, Heroku's documented route is heroku pg:backups:restore from a file at a URL. A direct pg_restore against the database URL with --no-owner also works. See restoring a .dump file on Windows.
How PostgresBackup handles Heroku
Paste the postgres://… URL into PostgresBackup (add ?sslmode=require) and schedule a nightly job. When Heroku rotates the credentials, the failed run is recorded in Backup History and the Activity Log. Edit the server's connection string once and the next run picks it up. The backups are standard .dump files, the same format as pg:backups.