Back to Blog
DevOps11 min read2026-07-10

How to Migrate from Heroku to a Modern PaaS

Heroku pioneered git-push deploys, but its pricing and stagnation have pushed many teams to look elsewhere. This guide is a practical, step-by-step migration plan with zero-downtime database cutover.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

# How to Migrate from Heroku to a Modern PaaS

Heroku invented the developer experience we now take for granted: git push and your app is live. Credit where due — the Heroku model shaped a generation of platforms. But for many teams, the removal of free dynos, rising costs, and a long period of perceived stagnation have made it worth re-evaluating. This is a practical, careful migration guide. The goal is a clean move with no data loss and minimal downtime.

First: understand what you actually depend on

Before touching anything, inventory your Heroku setup. The pieces that matter:

  • Buildpacks — how your app is built. Most modern platforms support Dockerfiles and auto-detection (buildpacks/Nixpacks-style).
  • Procfile — your process types (web, worker, release).
  • Config vars — environment variables (heroku config).
  • Add-ons — Postgres, Redis, scheduler, mail, etc. These are the trickiest part.
  • Dyno formation — how many of each process type and at what size.
# Capture everything before you migrate
heroku config --app my-app -s > heroku-config.env
heroku ps --app my-app
heroku addons --app my-app

Map Heroku concepts to a modern PaaS

HerokuModern PaaS equivalent
Dyno (web)Container app / web service
Dyno (worker)Background worker service
Procfile web:Start command (auto-detected or set)
BuildpackAuto-buildpack detection or Dockerfile
Heroku PostgresManaged PostgreSQL
Heroku RedisManaged Redis
Heroku SchedulerCronjobs
Config varsEnvironment variables
heroku domainsCustom domains + automatic SSL

The mental model carries over almost one-to-one, which is why the migration is usually less scary than it looks.

Step 1: get the app building elsewhere

Most modern platforms auto-detect Node/Python/Go/etc. and build a container for you. If your app already ran on Heroku buildpacks, it almost certainly builds with zero changes. If you want full control, add a Dockerfile:

FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV PORT=8080
EXPOSE 8080
CMD ["node", "server.js"]

The one detail to honor: read the port from the PORT env var, exactly as Heroku required. Modern platforms inject a port the same way.

Step 2: recreate config vars

Port your environment variables. Do not copy secrets into git — set them in the new platform's env var UI or CLI. Watch for Heroku-specific vars (like DATABASE_URL formats) you may need to adjust.

Step 3: provision the database (the careful part)

This is where migrations go wrong. Plan a real cutover.

  1. 1Provision a managed Postgres on the new platform.
  2. 2Put the app in maintenance mode or a brief read-only window to stop writes.
  3. 3Dump and restore:
# Dump from Heroku
pg_dump $(heroku config:get DATABASE_URL --app my-app) \
  --no-owner --no-acl -Fc -f heroku.dump

# Restore into the new managed database
pg_restore --no-owner --no-acl -d "$NEW_DATABASE_URL" heroku.dump
  1. 1Verify row counts on key tables before proceeding.
  2. 2Point the app at the new DATABASE_URL and deploy.

For large databases where a maintenance window is unacceptable, use logical replication to stream changes to the new DB, then flip over with seconds of downtime. For most small/medium apps, a brief maintenance window during low traffic is simpler and safe.

Step 4: workers and scheduled jobs

Recreate your worker dynos as background services and your Heroku Scheduler jobs as cronjobs. Verify each one runs in the new environment before decommissioning the old.

Step 5: DNS cutover with no downtime

  1. 1Deploy the new app and test it on its platform-provided URL.
  2. 2Add your custom domain to the new platform and let it provision SSL.
  3. 3Lower your DNS TTL a day in advance (e.g. to 300s) so the switch propagates fast.
  4. 4Run both old and new in parallel briefly.
  5. 5Update the DNS record to point at the new platform.
  6. 6Watch logs and metrics; keep Heroku running until traffic has fully drained.
  7. 7Decommission Heroku.

A migration checklist

[ ] App builds on new platform (Dockerfile or auto-detect)
[ ] All config vars / secrets recreated
[ ] Managed Postgres provisioned and dump restored + verified
[ ] Redis / other add-ons recreated
[ ] Worker processes running
[ ] Scheduled jobs (cron) running
[ ] Custom domain added + SSL active
[ ] DNS TTL lowered ahead of cutover
[ ] Parallel run validated
[ ] DNS switched, logs/metrics healthy
[ ] Heroku decommissioned

Why teams pick PandaStack for this

The Heroku model maps cleanly onto PandaStack: connect your Git repo and it builds, deploys, and goes live — "Push code. It runs." Workers become container services, Heroku Scheduler becomes cronjobs, and a managed PostgreSQL is auto-wired with DATABASE_URL injected for you, just like a Heroku add-on. You also get custom domains with automatic SSL, live build/app logs, rollbacks, and deploy history. The free tier (5 web services, 5 static sites, 1 database, edge functions) is enough to validate a migration end-to-end before you commit.

References

  • [Heroku Dev Center: Procfile](https://devcenter.heroku.com/articles/procfile)
  • [Heroku: exporting your database (pg:backups)](https://devcenter.heroku.com/articles/heroku-postgres-backups)
  • [PostgreSQL pg_dump documentation](https://www.postgresql.org/docs/current/app-pgdump.html)
  • [PostgreSQL logical replication](https://www.postgresql.org/docs/current/logical-replication.html)

---

Migrating off Heroku is mostly a careful checklist, not a rewrite. PandaStack gives you the same git-push simplicity with auto-wired databases — try the migration free at [dashboard.pandastack.io](https://dashboard.pandastack.io).

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in DevOps

Browse all DevOps articles →

See also