---
title: "Migrate from Heroku"
description: "Move a Heroku app to dply: keep your Procfile, copy config vars, replace add-ons with dply resources, move the database, and switch the domain."
---

This guide is for apps that run on Heroku dynos today: Rails, Node, Python, PHP, Go, Java and the other stacks Heroku buildpacks build. On dply they run as [container apps](/docs/containers) that sleep when idle, with managed databases and Valkey attached as resources. Your `Procfile` keeps working, so most apps move without code changes.

## How the pieces map

| Heroku | dply |
|---|---|
| App | A container app |
| Buildpacks | A generated image for the detected stack, or your own `Dockerfile` ([Frameworks & runtimes](/docs/frameworks)) |
| `Procfile` `web` | The app's start command ([Procfile](/docs/configuration-files#procfile)) |
| `Procfile` `release` | The release command: runs before the new version takes traffic |
| `Procfile` `worker` | **Queue workers** ([Queue workers](/docs/queue-workers)) |
| Dynos and dyno types | Instances and instance size ([Scaling & sleep](/docs/scaling-and-sleep)) |
| Eco dynos sleeping | **Sleeps after**: idle instances sleep and wake on the next request |
| Config vars | **Environment** ([Environment variables](/docs/environment-variables)) |
| Heroku Postgres | dply Postgres ([Postgres, MySQL & MongoDB](/docs/resources/databases)) |
| Heroku Data for Redis | dply Valkey ([Valkey (Redis)](/docs/resources/valkey)) |
| Other add-ons | A dply [resource](/docs/resources) where one exists; otherwise keep the provider and copy its variables |
| Heroku Scheduler | A **Scheduled task** ([Scheduled tasks](/docs/scheduled-tasks)) |
| `heroku run` | The app's **Console**, or `dply-edge ssh` from the [CLI](/docs/cli) |
| Review apps | [Preview deployments](/docs/preview-deployments) |
| `*.herokuapp.com` | The app's `*.on-dply.live` hostname ([Domains](/docs/domains)) |
| `heroku releases:rollback` | **Roll back** ([Deployments](/docs/deployments#roll-back)) |
| `Aptfile` | Not read. List the packages under `container.packages` in `dply.yaml` ([OS packages](/docs/containers#os-packages)) |

## Differences to plan for

- **`web` must listen on `$PORT`.** dply sets `PORT` (8080 for generated images), as Heroku does.
- **PHP apps ignore `web`.** dply runs the PHP server itself, so `heroku-php-apache2` and `heroku-php-nginx` lines are skipped. See [Deploy a Laravel app](/docs/guides/laravel).
- **Workers start when you add them.** A `worker:` line is only offered as the workers' command; nothing runs until you add queue workers.
- **No persistent disk.** As on Heroku, the filesystem resets on restart. Keep uploads in [object storage](/docs/resources/object-storage).

## 1. Deploy the app

1. Choose **New app**, connect your source control, and select the repository and branch Heroku deploys.
2. Check that dply detects the stack and shows **App**, then choose **Deploy**.

The build log shows `[Procfile] release: …` when it picks up your release command.

## 2. Copy config vars

Export them in `.env` format:

```bash
heroku config -s -a your-app > heroku.env
```

Delete the lines dply sets for you from the resources you attach next, such as `DATABASE_URL` and `REDIS_URL`, and Heroku-only values. Then paste the rest into **Environment**, or push the file with the [CLI](/docs/cli):

```bash
dply-edge env push --file heroku.env
```

> [!WARNING]
> `env push` replaces every variable on the app with the file's contents. Run it before you add variables by hand.

## 3. Attach resources

On the app's **Overview**, choose **Add resource** and add a **Database** (Postgres), **dply Valkey** for Redis, and **Queue workers** if your `Procfile` has a `worker` line. In the workers' settings, choose **Use Procfile: worker (…)** to use that command. Redeploy so the app receives the new variables.

## 4. Move the database

1. Put the Heroku app in maintenance mode: `heroku maintenance:on -a your-app`.
2. Capture a backup and download it. Heroku backups are `pg_dump -Fc` files:

   ```bash
   heroku pg:backups:capture -a your-app
   curl -o app.dump "$(heroku pg:backups:url -a your-app)"
   ```

3. Open the database from the app's **Overview**. Under **Export and import**, choose **Get upload command**, run it where `app.dump` is, then choose **Load it**.

See [Postgres, MySQL & MongoDB](/docs/resources/databases#export-and-import).

## 5. Switch the domain

1. Check the app on its `on-dply.live` address.
2. Add the domain under **Routing**, **Domains**.
3. Lower the TTL on the existing record, then point it at the CNAME target dply shows instead of the Heroku DNS target.
4. Choose **Verify DNS** and wait for TLS to be active.
5. Remove the domain from Heroku, then scale the Heroku dynos to zero.

See [Domains](/docs/domains).

## Next steps

- [Container apps](/docs/containers)
- [Scaling & sleep](/docs/scaling-and-sleep)
- [Troubleshooting builds](/docs/guides/troubleshooting-builds)
