Skip to content

Migrate from Heroku

PaaS Runtime is a Heroku-compatible platform — most Heroku apps migrate in under an hour. This recipe walks through every step.

Overview

The platforms share their core mental model (apps, dynos/processes, addons, config vars, releases, Procfile, buildpacks) so the surface mapping is mechanical:

Heroku command PaaS Runtime equivalent
heroku login paas login
heroku apps:create my-app paas apps create my-app
heroku git:remote -a my-app paas apps:remote my-app (auto on create)
heroku config:set KEY=val paas config:set KEY=val
heroku config:set --app my-app KEY=val paas config:set KEY=val --app my-app
heroku addons:create heroku-postgresql:standard-0 paas addons:create database:standard
heroku addons:create heroku-redis:premium-0 paas addons:create valkey:standard
heroku ps:scale web=3 worker=2 paas ps:scale web=3 worker=2
heroku logs --tail paas logs --tail
heroku run bash paas run -- bash
heroku domains:add www.example.com paas domains:add www.example.com
heroku certs:auto:enable TLS is auto-enabled (cert-manager + Let's Encrypt)
heroku releases paas releases
heroku rollback v42 paas releases:rollback v42
heroku ps:exec paas shell (interactive WebSocket)
heroku pipelines:promote git push paas main from staging branch

Procfile compatibility

Your existing Procfile works as-is:

web: bundle exec puma -p $PORT
worker: bundle exec sidekiq
release: bundle exec rails db:migrate
clock: bundle exec clockwork lib/clock.rb

PaaS recognizes the same process types (web, worker, release, plus the cron-* family for scheduled jobs). The $PORT env var is injected identically.

Add-ons mapping

Heroku Postgres → PaaS PostgreSQL (CNPG)

# Create the addon
paas addons:create database --type postgres --plan standard --name db
Heroku plan PaaS plan Memory HA Backups
Hobby Dev (free) free 256Mi none none
Hobby Basic starter 512Mi none nightly
Standard 0 standard 1Gi primary + replica WAL stream + nightly
Premium 0 pro 4Gi primary + 2 replicas WAL + cross-region

Migrate your data with pg_dump / pg_restore:

# 1. Dump from Heroku
heroku pg:backups:capture
heroku pg:backups:download -o heroku.dump

# 2. Restore into PaaS
DATABASE_URL=$(paas addons:credentials db --raw)
pg_restore --no-owner --clean --no-acl --dbname="$DATABASE_URL" heroku.dump

Heroku Redis → PaaS Valkey

paas addons:create cache --type valkey --plan starter --name cache

Valkey is wire-compatible with Redis 7. REDIS_URL is injected; rename to VALKEY_URL if needed:

paas config:set REDIS_URL="$(paas addons:url cache)"

Heroku Scheduler → cron processes

Heroku Scheduler jobs become entries in paas.toml:

[cron]
nightly_report = { schedule = "0 2 * * *", command = "rake report:nightly" }
hourly_sync = { schedule = "0 * * * *", command = "rake sync" }

Each entry becomes a Kubernetes CronJob in your tenant namespace.

Step-by-step migration

1. Authenticate

paas login
paas whoami   # confirms identity

2. Create the app

paas apps create my-app
git remote -v  # paas remote auto-added

3. Push your code

git push paas main

The Paketo buildpack picks up the same project (Gemfile, package.json, requirements.txt, etc.) Heroku used. First build is slower (~60–120s); subsequent pushes use cached layers.

4. Set environment variables

Bulk-import from your Heroku app:

heroku config --app my-heroku-app -s > heroku.env
# Inspect heroku.env, strip Heroku-specific vars (PORT, DYNO, etc.)
paas config:import heroku.env

Or set individually:

paas config:set NODE_ENV=production LOG_LEVEL=info
paas secrets:set STRIPE_API_KEY=sk_live_...

5. Add add-ons

paas addons:create database --type postgres --plan standard --name db
paas addons:create cache --type valkey --plan starter --name cache

A new release is triggered on each addon attach (env vars injected). Verify via paas config.

6. Verify

paas logs --tail               # watch the rollout
curl https://my-app.runtime.di2amp.com/healthz
paas releases                   # confirm the new release is `active`

Heroku buildpacks compatibility

PaaS uses Paketo Cloud Native Buildpacks, an OCI-compliant evolution of the Heroku Buildpacks line. The CNB spec is identical, so language detection and configuration env vars carry over:

  • BP_NODE_VERSION=20 (was NODE_VERSION on Heroku)
  • BP_PHP_VERSION=8.3 (was PHP_VERSION)
  • BP_GO_VERSION=1.22 (was GOOSE_VERSION)

Custom buildpacks declared in app.json buildpacks field require a manual port — the spec moved from heroku-buildpack (HBP) to CNB. Most major buildpacks have CNB equivalents.

Caveats

  • Heroku regions (us, eu) → PaaS regions (fr-par-1, fr-par-2, de-fra-1). Pick the closest in [app] region = "...".
  • Free tier limits are similar but not identical (PaaS: 30 min sleep, 512Mi RAM, 0.1 CPU shared).
  • Heroku Connect (Salesforce sync) has no PaaS equivalent — use the Salesforce REST API directly.

See also