How to export your data from Heroku

Heroku documents export paths for Postgres backups, direct pg_dump, config vars and app metadata, each with size, retention and URL-expiry limits. This page maps what you can take out, what you cannot, and the order to do it in.

On this page

What you can export from Heroku and what you cannot

Heroku Postgres is the part of your data with the most documented export path. Run heroku pg:backups:capture to take a snapshot, then heroku pg:backups:download to fetch it. PGBackups writes the pg_dump custom format, which pg_restore can load into another PostgreSQL installation, so the dump is not tied to Heroku. Plan around three limits: the link from pg:backups:url expires after 60 minutes, PGBackups is not supported on Heroku Postgres Advanced, and Heroku advises it only for moderately loaded databases up to 20 GB. Retention is 5 manual backups on Essential, 25 on Standard and 50 on Premium, and every snapshot is stored in the U.S. regardless of database region. Check your plan and database size before you rely on it. PGBackups documentation

For larger databases or partial exports, run pg_dump directly against the connection string, using --schema or --table to limit the output. Config vars can be listed with the heroku config CLI command, but the combined keys and values are capped at 64 KB per app. Confirm you have copied every value, including credentials, into your own secret store and not only into a local file. App metadata, such as formation, add-ons and collaborators, comes from the Platform API. Each account gets at most 4,500 request tokens, refilled at roughly 75 per minute, so a scripted export across many apps must be paced and retried.

Several categories have no equivalent export path in the sources reviewed here. Build artifacts, the contents of dyno filesystems, log history and the internal state of add-ons other than Postgres need separate handling. Keep application source and the Procfile in Git, then confirm that your target build reproduces the runtime files your app depends on. Confirm log retention and each add-on's data export options with Heroku and the add-on provider before your renewal date. The export is only the first step of an exit. Restore it into a test environment, verify row counts and application behavior, and then plan the cutover, DNS changes and the backups you will own afterward.

Which Heroku data exports, by method, format and limit

This table compares the main ways to pull data out of Heroku, including Postgres backups, direct dumps, config vars and Platform API metadata, with the format and limit that applies to each. Read each row as a checklist item for your exit plan, and confirm the limits against your own plan and account before you rely on them.

Data and methodFormat and commandLimit or constraint to plan for
Postgres backup (PGBackups)pg:backups:capture then pg:backups:download; pg_dump custom format, restored with pg_restoreSuited to moderately loaded databases up to 20 GB; larger or heavily loaded databases can time out
Backup download linkpg:backups:url returns a public download URLThe URL expires after 60 minutes, so script the download in the same session
Manual backup retentionSnapshots kept until you delete them, by plan5 on Essential, 25 on Standard, 50 on Premium; download copies before the count is reached
Scheduled daily backupsAutomatic captures stored on HerokuKept for 7 days, so older points in time need your own copies
Direct pg_dumpConnect with the database connection string; plain-text or partial output, with --schema or --tableUse for partial or readable exports; confirm connection and timeout behavior for your database size
Config varsheroku config CLI command lists keys and valuesCapped at 64 KB of combined keys and values per app; store the output in your secrets manager
App metadata via Platform APIScripted calls to api.heroku.com for app, formation and add-on detailsEach account has 4,500 request tokens, refilled at about 75 per minute, which slows large scripted exports
PGBackups on Heroku Postgres AdvancedNot supported; use pg_dump or logical backups insteadPlan the export path before you upgrade a database tier
Backup storage locationSnapshots captured with pg:backups are stored in the U.S.Check this against data residency rules, whatever the database region

What does not export cleanly and how to work around it

Heroku's export tools cover Postgres dumps, config vars and app metadata, but several things a running app depends on have no direct export. These cards cover the built artifact, the dyno filesystem, log history, add-on state and Heroku-specific delivery features, and what to rebuild or confirm for each.

Built container image or slug

A Heroku build produces a slug or image that you cannot download as a portable artifact through the documented export paths. Treat the Git repository as the source of truth and rebuild on the target platform. Dokku can build with Cloud Native Buildpacks through the pack CLI, which uses the same buildpack family Heroku maintains for Fir. Confirm with Heroku support whether any slug or image download is offered for your stack, and record the exact stack and buildpack versions so the rebuild matches what runs in production.

Dyno filesystem and uploaded files

Dyno filesystems are ephemeral, so files written at runtime, such as user uploads or generated reports, are not part of any Heroku export. Find where the app writes them and list every path. Copy the objects into S3-compatible storage you control, then change the app to read from that bucket. Check whether the app also keeps state in /tmp or in a local cache, since those files disappear on restart and are not worth migrating.

Log history beyond what you streamed

Heroku log retention is limited, and the platform does not provide a bulk export of past log lines. Logs you did not capture cannot be recovered later, so the exit plan should start streaming now. Run a log drain or a heroku logs --tail process into storage you own, and keep audit-relevant entries for the period your compliance owner requires. Confirm with Heroku support what history, if any, can be retrieved for your apps before the cutover date.

State held by add-ons beyond Postgres

Postgres has a documented export path, but other add-ons, such as Redis, queues, search indexes, email or scheduler configuration, keep state that the Heroku CLI does not dump. Inventory every attached add-on with heroku addons, then decide per service whether the data is durable or a cache. Caches can be rebuilt. Queued jobs, sessions and search indexes usually need a plan: drain queues before cutover, or rebuild indexes from source data. Confirm each vendor's own export option with that vendor.

Pipelines and Review Apps logic

Heroku Pipelines, promotions and Review Apps are configured in the Heroku dashboard and are not exported as code. Write down each pipeline stage, its promotion rule and the app.json settings that create review apps. Recreate that logic as a CI workflow that builds once, runs tests and deploys to the target platform. Review apps map to Coolify preview deployments or Dokku deploy branches. Confirm with Heroku support whether pipeline configuration can be exported in any form.

Scheduler and dashboard-only settings

Heroku Scheduler jobs, custom domains, SSL certificates, team membership and dashboard notification settings live in the Heroku account rather than in the repository. Capture each one in a table during the inventory, including the job command, frequency and owner. Scheduled jobs must be recreated as cron tasks on the new host and tested before the old schedule is disabled, so no job runs twice or never. Certificates must be reissued on the target, so plan the DNS timing around them.

The export sequence and how to verify each step

This sequence turns Heroku's app, database and configuration state into copies you hold, and each step closes with a check that proves the copy is usable before you rely on it. Run it in the order below so that the database snapshot, config record and metadata all describe the same point in time.

  1. Inventory apps and add-ons

    The account owner lists every app, process type, add-on and attached database across the team, using the Heroku CLI or the Platform API. Record the app name, region, stack, database plan and each add-on's resource name in a register that the migration team can read. The step is done when every app and database in scope appears in the register and each owner has confirmed the list. Anything missing here is missing from the exit plan.

  2. Freeze a capture point for each database

    Agree a write window with the application owners so that the database does not change between the snapshot and the cutover. Pause scheduled jobs and background workers that write to the database for the same window. This step has no tool output of its own, so record the time the freeze began and ended in the register. Confirm with each owner that the application has stopped accepting writes before the capture runs.

  3. Capture and download PGBackups snapshots

    Run heroku pg:backups:capture against each database, then fetch the result with heroku pg:backups:download, which writes a file in pg_dump custom format. Heroku advises PGBackups only for moderately loaded databases up to 20 GB, so for larger or heavily loaded databases use the direct pg_dump route in the next step. Note the backup identifier, the file size and a SHA-256 checksum computed on your own machine. The step is done when the local file's checksum is recorded alongside the backup ID.

  4. Run pg_dump for partial or plain-text exports

    Where you need a plain-text dump, a single schema or a subset of tables, or where PGBackups is unsuitable, run pg_dump directly against the database connection string with options such as --schema or --table. Heroku documents this route for these cases. Record the pg_dump version, the exact command and the output checksum. Confirm the file is complete by checking that the final lines of a plain-text dump are present and that the command exited with status zero.

  5. Export config vars and store them securely

    Run heroku config for each app to list its config vars, and write the output to a file in an encrypted secrets store or an access-controlled vault, not to a shared document or a chat thread. Heroku caps combined keys and values at 64 KB per app, so the file is small, but it contains credentials. Count the keys in the file and compare the count with the number shown in the Heroku dashboard or the output of the CLI. The step is done when both counts match and the file's location and access list are recorded.

  6. Pull app metadata through the Platform API

    Script calls to the Platform API at api.heroku.com for app settings, formation, add-on attachments and collaborators, and save each response as JSON. Each account has a bounded request-token budget that refills at a fixed rate, so batch requests, pause when responses slow, and keep retries to a minimum. The step is done when the JSON files cover every app in the register and each file parses without error. Compare the app count in the files with the count from the inventory.

  7. Restore the dump into a scratch PostgreSQL

    Install a scratch PostgreSQL server of the same major version as the source, or a newer one, and run pg_restore on the custom-format file into an empty database. Then compare row counts and checksums per table against the source, using a query that aggregates each table's primary keys or a hash of its rows. Any mismatch means the capture is not usable, so repeat the capture before moving on. Keep the restore log with the register as evidence.

  8. Copy everything to storage you control

    Upload the custom-format dumps, plain-text dumps, config var file and metadata JSON to storage you own, such as an object store or a server in the target environment. Heroku's download URLs expire after 60 minutes, so generate each heroku pg:backups:url link only when you are ready to fetch it, and never rely on a link that was generated earlier. Verify each uploaded object by comparing its checksum with the value recorded at download. The step is done when every artifact in the register has a matching checksum in your storage.

Where the exported data goes: restoring into PostgreSQL you control

The pg_dump custom format produced by heroku pg:backups:capture can be restored with pg_restore into any PostgreSQL installation you control, so the export is only half the move. The two destinations in this pillar are the official dokku-postgres plugin on a Dokku host and a PostgreSQL database that Coolify deploys and manages. Both run standard PostgreSQL, so a dump taken from Heroku Postgres restores into either one without conversion. Before you pick a target, confirm the exact PostgreSQL major version each option installs on the day you plan to move, because the plugin and Coolify templates can change between releases.

Choose the destination major version on the day of the move, not weeks earlier. Supported PostgreSQL versions are 18 (final release November 14, 2030), 17 (to November 8, 2029), 16 (to November 9, 2028), 15 (to November 11, 2027) and 14 (to November 12, 2026). Version 14 support ends in November 2026, so a target that is still on 14 will need a major upgrade soon after cutover. Check the Heroku Postgres version you are exporting from, confirm the restore works on the chosen version, and check whether your application's driver and extensions support it. Logical replication can carry data between major versions with only seconds of downtime if you need a newer target later.

A successful test restore does not prove a production cutover is safe. A test shows that the dump is readable and the schema loads, but it does not show how the application behaves against the new host, how long a fresh dump takes on your data, or whether writes made after the snapshot are captured. Plan a dated rehearsal, measure restore time, run your application's read and write paths against the restored copy, and decide in advance how writes during the cutover window will be handled. Record which backup and which restore version you tested, so the production run repeats a known procedure.

What to plan before the Heroku contract or renewal ends

Exporting from Heroku is less about one command than about the calendar around it. Backups age out, snapshot storage sits in the U.S., and dynos can be scaled down before you have verified a single restore. The cards below list the decisions and checks to settle before your Heroku term or renewal date arrives.

Time for a full capture cycle

Plan for a complete cycle, not a single download: capture, download, restore into a scratch PostgreSQL, run application checks, then repeat for the final cutover. Schedule at least one full rehearsal well before the end date so a failed restore still leaves room to fix it. Confirm with your team how long a download and restore take for your largest database, since that time depends on your data volume and network path.

Retention windows set the deadline

Manual backups are kept in limited numbers, and scheduled daily backups are kept for a fixed window, so older snapshots disappear without a separate warning from your side. Count your manual backups against your plan tier and export the ones you need before new captures push them out. Treat the scheduled backup window as the most recent recovery point you can count on.

Plan tier and PGBackups availability

Check which Heroku Postgres plan each database runs on before you start, because the manual backup allowance differs by tier and PGBackups is not supported on Heroku Postgres Advanced. If a database is on Advanced, plan a direct pg_dump from the connection string instead. Record the tier in your export inventory so the method is chosen per database.

Check the 20 GB guidance

Heroku advises PGBackups only for moderately loaded databases up to 20 GB, because backups can time out on heavily loaded or larger databases. Measure each database's size and load against that guidance. Databases above it, or busy during capture, need a direct pg_dump or logical backup approach that you test before the deadline.

U.S. storage of snapshots

Heroku's documentation states that all snapshots captured with pg:backups are stored in the U.S., regardless of where the database is located. If your data residency rules require EU or other regional storage, decide how exported files are handled from the moment they are downloaded. Confirm with your compliance owner whether that U.S. location is acceptable for each dataset.

Final export and written end-of-service terms

Keep one final export taken after dynos are scaled down and traffic stops, so no writes are lost between the last capture and shutdown. Ask Heroku for the written end-of-service and data deletion terms, including how long your data remains after the contract ends and how deletion is confirmed. Store those terms with the export records.

Questions teams ask before exporting Heroku data

How much effort does a full Heroku data export take?

The effort depends mainly on what you hold on Heroku, and a full export is several smaller jobs rather than one command. The main drivers are the size and load of your Heroku Postgres databases, the number of add-ons that hold data, the number of config vars across each app, and how much app metadata you need to keep. A small app with one database and a short list of config vars is a short job. A larger estate with several databases, Redis or other add-ons, and many apps needs an inventory first, so each data store has a named export method and a named check. Confirm the inventory with your team before you estimate anything.

Can our team export Heroku data alone, or do we need a partner?

Many teams can export the core data themselves, since Heroku documents the commands for Postgres backups, direct pg_dump runs, config vars and the Platform API. The harder part is confidence: proving that every database, secret and metadata record moved, and that the restored copy works. A partner can take on that work under a fixed scope, with deliverables and timelines agreed in writing before work starts. DeployOpen implements, integrates, backs up and documents self-hosted platforms in your own environment, so a fixed-scope engagement would cover the export plan, the restore into your target platform, the verification steps and the handover. Ask for the written scope and the list of deliverables before you commit.

How long does the export work take, and what extends it?

The duration depends on your environment and the volume of data, so confirm a timeline against your own inventory rather than relying on a general figure. The factors that most often extend the work are a database that is too large or too busy for PGBackups, which pushes you to a direct pg_dump, a Platform API export that must stay within request limits, large numbers of config vars or add-ons to recreate, and the time needed to agree a maintenance window with the business. Restore testing also takes time, and a second restore run is common when a first attempt exposes a missing extension or a schema mismatch. Ask any partner to show the dependencies and the verification checkpoints in the plan.

What do we need to provide for an export engagement?

Plan on providing access, a maintenance window and a named owner who signs off the verification. Access means Heroku account rights to run the export commands and read config vars, plus credentials or a host for the target platform. Heroku PGBackups snapshots are stored in the U.S. regardless of database location, so your data residency owner should review where copies land before the export starts. The maintenance window matters for write-heavy databases, because a backup taken during writes captures one moment in time and cutover needs a plan for changes made after the snapshot. Sign-off should cover row counts or checksums, a restored application test, and confirmation that secrets were moved to a store you control.

Can PGBackups files be restored outside Heroku?

Yes. PGBackups uses the pg_dump custom format, and Heroku documents that this format can be restored into another PostgreSQL installation with pg_restore. You create a capture with the pg:backups command, download it, and restore it into a PostgreSQL server you run. Two limits apply. First, the public download URL from pg:backups:url expires after 60 minutes, so script the download promptly. Second, PGBackups is not supported on Heroku Postgres Advanced, so check your plan before you rely on it. Confirm the PostgreSQL major version on the target matches or exceeds the source, and test restore on a copy before you retire the Heroku database.

What happens if a backup times out on a database over 20 GB?

Heroku advises PGBackups only for moderately loaded databases up to 20 GB, because backups can time out on heavily loaded or larger databases. If a capture times out, you have no usable snapshot from that attempt, so the data is not lost from Heroku but it is not exported either. Heroku points larger databases to logical backups, and you can run pg_dump directly against the connection string for plain-text or partial dumps, using options such as --schema or --table. Plan a dump window, check the target has enough disk space, and verify row counts after restore. Confirm the approach with your database owner before the cutover date is set.

How long does Heroku keep backups, and what does that mean for the exit plan?

Heroku keeps manual backups by plan: 5 on Essential, 25 on Standard and 50 on Premium, and scheduled daily backups are kept for 7 days. Those limits set how far back you can roll data, so do not rely on Heroku as your only archive. Download and store the snapshots you need in a location you control before you change or downgrade a plan. Your retention policy for the new platform should cover the window you need for audit and recovery, which you set with your compliance owner. Confirm your current plan's retention in the Heroku PGBackups documentation before you start.

Does the Platform API give us a full copy of app metadata?

The Platform API returns app, formation, add-on and config data, but scripted exports are bounded by rate limits. Each account has at most 4,500 request tokens, refilled at roughly 75 per minute, so a large inventory must be paced across runs. Export metadata into version-controlled files, record the timestamp, and compare counts against the Heroku dashboard. Confirm with your engineers which fields you need for the target platform, since the API does not map one to one onto every new deployment model.

Keeping the data usable after the export

A verified export is not yet a working system. Once the dump file and config vars are in hand, the team still has to rebuild how the app is built on the new host. On Heroku, buildpacks produce the slug; on Coolify, the equivalent decision is between Nixpacks, Railpack (beta), Static, Dockerfile or Docker Compose, and on Dokku it is between the herokuish builder, Cloud Native Buildpacks through the pack CLI, a Dockerfile, or the Nixpacks and Railpack builders. Pick the build method first, run a build of the existing commit, and confirm the process types, Procfile behavior and any release step before any traffic moves. Re-enter every config var as a platform environment variable, and check that secrets were not copied into logs or shared chat during the export.

The larger gap is operational. Restoring a pg_restore archive proves that the data can be read, but it does not create a backup schedule, retention policy or restore test on the destination. Coolify's guidance for teams coming from managed platforms makes the point directly: a rollback can redeploy a retained application image, but it does not restore databases or persistent files. Your team owns the database backup schedule, off-host copies, restore drills and PostgreSQL upgrades from this point on. Ask who will run those tasks, how often a restore will be tested, and where backups will be kept outside the server that hosts the database.

Decide the proxy and scheduler before go-live, since both shape routing and failure recovery. Coolify runs one coolify-proxy container per server with Traefik by default or Caddy as an alternative, while Dokku ships nginx by default with several alternatives documented in its proxy management guide. Dokku's default scheduler runs containers on the single host, so a host failure is an outage until you have a recovery plan. Confirm the scheduler and proxy choices against the current documentation for your chosen platform and version, and rehearse a full restore on a scratch server before the Heroku app is shut down.

How DeployOpen scopes and delivers a Heroku data exit

DeployOpen treats a Heroku data exit as a written, fixed-scope engagement covering Postgres dumps, config vars, app metadata and the limits that shape the timeline. We plan, build, test and document the export path inside your own infrastructure, then hand over evidence your team can rerun.

How we scope

  • Inventory every Heroku app, Postgres database, add-on and config var set that must leave the platform
  • Record each database plan tier and confirm whether PGBackups is available or a direct pg_dump is needed
  • Agree in writing which data is exported, where it lands, and who signs off on completeness

How we build

  • Build and test pg:backups capture and download runs, and a direct pg_dump path for large or partial exports
  • Script config var and app metadata collection through the CLI and Platform API within documented rate limits
  • Restore every dump into a staging PostgreSQL instance on your servers and compare row counts and schemas

How we hand over

  • Deliver runbooks for export, transfer, restore and verification, with the expiring download URL steps spelled out
  • Document storage locations, retention settings and access controls for every exported artifact
  • Provide a checklist of items to confirm with Heroku and your own team before the source account is closed

How we support

  • Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts
  • Ongoing support, upgrades and continuous testing are available as a retainer after handover
  • Quotes follow from the written scope, so ask for each export, restore and verification step as a line item

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.