# Migrate from Heroku to DigitalOcean App Platform App Platform is a fully managed Platform-as-a-Service (PaaS) that deploys applications from Git repositories or container images. It automatically builds, deploys, and scales components while handling all underlying infrastructure. This guide walks you through migrating your Heroku-hosted applications and datastores to DigitalOcean App Platform. It covers the following: - How Heroku concepts map to DigitalOcean App Platform - How to model a Heroku app as one or more App Platform components - How to migrate your databases to DigitalOcean Managed Databases - How to cut over DNS and finalize your migration **Tip**: Many apps can be migrated automatically using AI agents. Read the [Migrate Heroku to App Platform Using AI](#migrate-with-ai) section for guidance and an example prompt. ## Why Migrate? DigitalOcean App Platform offers a compelling alternative to Heroku, particularly as your application scales and your infrastructure needs evolve. **Key advantages:** - **Cost savings:** Granular instance sizes from 512 MB to 32 GB of RAM at significantly lower price points. - **Flexible scaling:** Autoscaling with granular plan sizes, so scaling past 2.5 GB does not require moving directly to 14 GB. - **Growth path beyond PaaS:** Grow your application into DigitalOcean’s broader ecosystem with Managed Kubernetes, Spaces Object Storage, Gradient, Droplets, and more. - **Free support for all users:** Transparent, accessible support with clear upgrade paths and no enterprise sales process required. - **Modern developer experience:** Implement your infrastructure-as-code via [App Spec (YAML)](https://docs.digitalocean.com/products/app-platform/reference/app-spec/index.html.md), integrate with GitHub/GitLab/Bitbucket, auto-deploy on push, rollback when needed, and aggregate your monitoring with built-in log forwarding and health checks. - **Dedicated IP addresses:** Take advantage of free static IP addresses for each of your applications to make accessing your application straightforward and reliable. ## Concept Mapping Most Heroku concepts map cleanly to equivalent functionality on DigitalOcean App Platform. Use this table as a quick reference throughout your migration: | Heroku | DigitalOcean App Platform | |---|---| | App | App | | web process (Procfile) | Service (Web Service component) | | worker / non-web process | Worker component | | release phase | Job (PRE\_DEPLOY kind) | | Heroku Scheduler / clock process | Job (SCHEDULED kind, cron expression) | | Config Vars | Environment Variables / App-Level Env Vars | | Dyno | Container instance | | Dyno type (Eco, Basic, Standard, etc.) | Instance size (Basic, Professional, etc.) | | Heroku Postgres | DigitalOcean Managed PostgreSQL | | Heroku Key-Value Store (Redis) | DigitalOcean Managed Redis / Valkey | | Heroku Add-ons | DigitalOcean Marketplace / external services | | Pipelines (review, staging, prod) | App-level environments / Deploy to DO Button | | Procfile | App Spec (YAML) + Build/Run commands | | Buildpacks | Cloud-Native Buildpacks (auto-detected) | | heroku.yml / Docker deploys | Dockerfile-based builds | | Custom domains + ACM | Custom domains + auto-managed TLS certs | | Heroku CLI (heroku) | DigitalOcean CLI (doctl) | ## Step 1: Prepare to Migrate Before making any changes, take inventory of your Heroku resources and set up your DigitalOcean account. ### Create Your DigitalOcean Account Sign up at [`cloud.digitalocean.com`](https://cloud.digitalocean.com) if you haven’t already, then [install and configure the DigitalOcean CLI (doctl)](https://docs.digitalocean.com/reference/doctl/how-to/install/index.html.md) to manage resources from your terminal. Authenticate with `doctl` to log in to your account: ```shell doctl auth init ``` Then validate that `doctl` is working: ```shell doctl account get ``` Read [How to Install and Configure doctl](https://docs.digitalocean.com/reference/doctl/how-to/install/index.html.md) for more help with setting up `doctl`. ### Catalog Your Heroku Resources Identify each Heroku app you want to migrate. For each app, note the following: - **Procfile processes:** web, worker, release, clock, and any other processes defined - **Config Vars:** All environment variables (especially DATABASE\_URL, REDIS\_URL, and API keys) - **Add-ons:** Heroku Postgres, Heroku Key-Value Store (Redis), and any third-party add-ons - **Custom domains:** Any custom domains and their DNS configuration - **Buildpacks:** Which language buildpacks your app uses **Tip**: Many third-party add-ons still work after migration. If you’re using add-ons like SendGrid, Papertrail, or New Relic via API keys and environment variables, you can continue using them on DigitalOcean by copying over the relevant config vars as environment variables. ## Step 2: Create Your Datastores Create your DigitalOcean Managed Database clusters before deploying your app. This way, your services can connect on their first deployment. **Note**: This step provisions empty databases. Data migration happens in Step 4 during the final cutover. ### Provision a Managed PostgreSQL Database 1. In the DigitalOcean Control Panel, click on the **Create** menu, then click **Databases**. 2. Select **PostgreSQL** as the database engine. 3. Select a database configuration and storage size. Start small for testing, as you can scale up before the final migration. 4. Select a datacenter region (select the same region you plan to deploy your app in to minimize latency). 5. Click **Create Database Cluster**. Provisioning takes a few minutes. 6. Once ready, navigate to the database’s **Overview** page and scroll to **Connection Details**. Click the **Connection parameters** dropdown menu and select **Connection string**. Copy the connection string to add it to your app’s environment variables in the following step. ### Provision a Managed Valkey (Redis) Instance If your Heroku app uses Heroku Key-Value Store (Redis), follow the same process but select Valkey as the database engine. Copy the connection string for use as your `REDIS_URL` environment variable. **Tip**: DigitalOcean Managed Databases support private networking through VPC. When your App Platform app is in the same region as your database, traffic stays on the private network which is faster and more secure. To take full advantage of this feature, select the **VPC network** option in your database’s **Connection Details** panel and copy the private network connection string. Read the [Postgres](https://docs.digitalocean.com/products/databases/postgresql/concepts/best-practices/index.html.md#use-vpcs) and [Valkey](https://docs.digitalocean.com/products/databases/valkey/concepts/best-practices/index.html.md#use-vpcs) best practices documentation to use VPCs with managed databases. ## Step 3: Recreate Your App on App Platform Create the App Platform resources that correspond to your Heroku app’s processes. ### Understanding Your Procfile Most Heroku apps define a Procfile in the repo root. The following code listing shows an example Node.js Procfile and how each process maps to App Platform: ``` # Procfile web: npm run start worker: npm run worker release: db-migrate -e prod up ``` | Procfile Entry | App Platform Component | Notes | |---|---|---| | web | Service (Web Service) | Receives HTTP traffic, gets a public URL | | worker | Worker | Background processing, not internet-accessible | | release | Job (PRE\_DEPLOY) | Runs before each deploy (for example, DB migrations) | ## Deploy via the Control Panel ### Create a Web Service 1. In the DigitalOcean Control Panel, click **Create**, then **App Platform**. 2. Connect your **GitHub, GitLab, or Bitbucket** repository and select the branch to deploy. 3. App Platform auto-detects your language. Confirm the **Resource type** is set to **Web Service**. 4. Configure your **Build command** (`npm install`, for example) and **Run command** (`npm run start`). 5. Select your **Instance size**. 6. Add **Environment variables**: copy over your Heroku config vars. Set `DATABASE_URL` to the connection string you copied from your database’s **Connection Details** in Step 2. 7. Select your **Datacenter region** (match your database region). 8. Review your configuration, then click **Create app**. ### Add Worker Components For each non-web process in your Procfile, add a Worker component to the same app: 1. From your app’s dashboard, click **Add components**, then **Create resources from source code**. 2. Select the same repository and branch. 3. Change the **Resource type** to **Worker**. 4. Set the **Run command** to your worker’s start command (for example `npm run worker`). 5. Configure environment variables as needed, then click **Add resources**. ### Add a Pre-Deploy Job (Release Phase) To replicate Heroku’s release phase, add a Job component: 1. From your app’s dashboard, click **Add components**, then **Create resources from source code**. 2. Select the same repository and branch. 3. Change the **Resource type** to **Job**. 4. Set the **Job Trigger** to **Before every deploy**. This ensures the job runs before each deploy and blocks deployment if it fails, the same as Heroku’s release phase. 5. Set the **Run command** to your migration command (for example `db-migrate -e prod up`). **Tip**: If you use Heroku Scheduler or clock processes, create a Job with **Job Trigger** set to **On a Schedule**, and enter a cron expression (such as `0 */6 * * *` for every 6 hours). This runs directly on App Platform with no external scheduler needed. ## Deploy via App Spec (YAML) For infrastructure-as-code workflows, you can define your entire app in a single YAML file and deploy it using doctl. This is the App Platform equivalent of combining your Procfile + app.json into one declarative specification. **Example App Spec translating the example Procfile** ```yaml name: my-app region: nyc services: - name: web github: repo: your-org/your-repo branch: main build_command: npm install run_command: npm run start http_port: 8080 instance_size_slug: basic-xxs instance_count: 1 envs: - key: DATABASE_URL scope: RUN_TIME value: "${db.DATABASE_URL}" workers: - name: background-worker github: repo: your-org/your-repo branch: main build_command: npm install run_command: npm run worker instance_size_slug: basic-xxs jobs: - name: db-migrate github: repo: your-org/your-repo branch: main build_command: npm install run_command: db-migrate -e prod up kind: PRE_DEPLOY instance_size_slug: basic-xxs databases: - name: db engine: PG production: true cluster_name: my-db-cluster ``` Deploy with a single doctl command: ```shell doctl apps create --spec app-spec.yaml ``` **Tip**: To update existing apps from App Spec YAML, use the following doctl command: ```shell doctl apps update --spec app-spec.yaml ``` ### Set Environment Variables App Platform environment variables work similarly to Heroku config vars. A few things to note: - **Component-level vs. App-level:** Environment variables can be scoped to individual components or shared across all components in an app. - **Bindable variables:** When you attach a DigitalOcean Managed Database to your app, App Platform provides bindable variables (like `${db.DATABASE_URL}`) that automatically contain your connection details. The prefix (for example, `db`) is the database’s component name, that is, the `name` in your app spec’s `databases` array or the name shown for the database in the Control Panel. - **Build vs. Runtime scope:** You can specify whether a variable is available at build time, runtime, or both. - **Encryption:** Select **Encrypt** for sensitive values to store them securely. Encrypted variables cannot be read back from the dashboard. ## Step 4: Migrate Data and Cut Over With your App Platform app running and connected to its new (empty) databases, move your production data and switch traffic over. **Warning**: This final migration step requires downtime for your application. Schedule the cutover for off-peak hours to minimize impact on your users. ### Scale Up Your DigitalOcean Databases Before importing data, ensure your DigitalOcean databases have enough storage and compute for your production workload. You can resize database clusters in the Control Panel at any time. To prepare for production workloads, we strongly recommend taking this time to add at least one standby node to gain automatic failover. Refer to the How to Add Standby Nodes guides ([for Postgres](https://docs.digitalocean.com/products/databases/postgresql/how-to/add-standby-nodes/index.html.md) and [for Valkey](https://docs.digitalocean.com/products/databases/valkey/how-to/add-standby-nodes/index.html.md)). ### Enable Maintenance Mode on Heroku Put your Heroku app into maintenance mode so no new writes occur during migration: ```shell heroku maintenance:on --app ``` **Tip**: For databases larger than 1 TB, reach out to [DigitalOcean’s Migration program](https://www.digitalocean.com/migrate). Large database migrations can take a long time and may need special attention and planning, which DigitalOcean’s experts can help you evaluate and plan for. ### Migrate Your Database into DigitalOcean Managed Database DigitalOcean provides the following automated migration tools accessible from your DigitalOcean Databases panel or from our API. These tools transfer your data to DigitalOcean and keep it in sync with your source datastore for up to two weeks, letting you choose when to cut over. 1. [How to Migrate PostgreSQL Databases to DigitalOcean using Continuous Migration](https://docs.digitalocean.com/products/databases/postgresql/how-to/migrate/index.html.md) 2. [How to Migrate Valkey Databases to DigitalOcean](https://docs.digitalocean.com/products/databases/valkey/how-to/migrate/index.html.md) ### Verify Your Application Before switching DNS, verify that everything works correctly on your App Platform deployment: - Visit your app’s auto-assigned `.ondigitalocean.app` URL. - Test critical user flows and API endpoints. - Verify database connectivity and data integrity. - Check that background workers are processing jobs correctly. - Review application logs in the App Platform dashboard. ### Update Your DNS Records If your Heroku app uses a custom domain, [add your custom domain to your App Platform app](https://docs.digitalocean.com/products/app-platform/how-to/manage-domains/index.html.md) in the Control Panel under the **Networking** tab. Then update your DNS records to point to DigitalOcean instead of Heroku: - **For apex domains:** Add an A record pointing to the IP provided by App Platform. - **For subdomains:** Add a CNAME record pointing to your app’s `.ondigitalocean.app` hostname. App Platform automatically provisions and manages TLS certificates for your custom domains. DNS changes can take up to 72 hours to propagate. You can track progress in the App Platform dashboard. ### Migrate CDN Traffic If your Heroku application uses a CDN (commonly via add-ons such as Expedited CDN or similar reverse-proxy CDNs), this functionality does **not** migrate automatically. You must explicitly reconfigure CDN delivery when cutting over to DigitalOcean. #### Remove Heroku CDN Dependencies Before cutover: - Disable or remove any Heroku CDN add-ons. - Remove any CDN-specific configuration that assumes Heroku headers, hostnames, or proxy behavior. - Confirm your application serves correct cache headers (`Cache-Control`, `ETag`, `Last-Modified`) for static assets. #### Verify CDN Behavior on App Platform After deploying your app to App Platform: - Access the default `*.ondigitalocean.app` URL. - Confirm that static assets are being served correctly. - Inspect response headers to ensure assets are cacheable. App Platform respects standard HTTP caching headers. If assets are not cached, review your application or framework configuration. ## AI-Assisted Migration: Heroku to App Platform The following prompt serves as a migration orchestrator. It guides an AI assistant to execute CLI commands in a structured sequence to help you inventory, map, and deploy your Heroku application to DigitalOcean. This is an assisted workflow designed for functional testing and staging environments. The process is designed for situations where you don’t have the source code locally. The agent is prompted to pull everything directly from Heroku, inventory your entire setup (dynos, environment variables, add-ons, domains, databases), and reconstruct it as a DigitalOcean app spec YAML file. The migration includes built-in checkpoints where you review and approve decisions before anything gets deployed or modified. ### Prerequisites The following CLI tools must be installed and authorized to their respective accounts: - `heroku`: Heroku CLI, [logged in to your Heroku account](https://devcenter.heroku.com/articles/authentication) (check with `heroku auth:whoami`) - `doctl`: DigitalOcean CLI, [authenticated with the target DigitalOcean team account](https://docs.digitalocean.com/reference/doctl/reference/auth/init/index.html.md) (check with `doctl account get`) - `gh`: GitHub CLI, [authenticated with the GitHub account containing your app repo](https://cli.github.com/manual/gh_auth_login) (check with `gh auth status`) - `git`: for cloning and pushing repositories - `psql` (and `pg_dump`/`pg_restore`): PostgreSQL client tools for database migration ### Supported App Types The prompt can migrate the following types of apps: - Web apps with a `Procfile` (such as Node.js, Python, Ruby, Go, or PHP) - Apps using Heroku buildpacks or Docker - Apps with any combination of web dynos, workers, clock processes, and release phase commands - Apps using Heroku Postgres, Redis, or third-party add-ons accessible via environment variables ### Repository Requirements The following code repositories are supported: - Apps connected to a GitHub repo (this is preferred, as App Platform can deploy directly from GitHub) - Apps with code only on [Heroku’s git remote](https://devcenter.heroku.com/articles/git) (the prompt handles pushing to GitHub) - Private or public repositories **Warning**: The following features are not supported by the migration prompt: - **Heroku Pipelines** or **Review Apps**: these need manual recreation. - **Heroku Scheduler**: must be mapped to DigitalOcean’s Scheduled Jobs or an external cron service. - **Heroku Private Spaces** or **Shield**: contact DigitalOcean for VPC equivalents. - **Multi-region** or **high-availability** database replication: plan and configure separately. - **DNS cutover**: the prompt explicitly leaves DNS changes to you. ```markdown Help me migrate my Heroku app to DigitalOcean App Platform using doctl. I don't have the source code locally, so we need to start from Heroku. Reference: https://docs.digitalocean.com/products/app-platform/getting-started/migrate-from-heroku/ ## Step 1: Discover my Heroku app Run these commands to inventory my Heroku app: - `heroku apps` — list my apps so I can tell you which one to migrate - Then for the app I choose, gather ALL of the following: - `heroku ps --app ` — dyno types and counts - `heroku config --app ` — all config/env vars - `heroku addons --app ` — all add-ons (DB, Redis, etc.) - `heroku domains --app ` — custom domains - `heroku apps:info --app ` — general app info (stack, git URL, region) - `heroku labs --app ` — any enabled labs features - `cat Procfile` (after cloning) — process types ## Step 2: Get the source code - Determine if the app is connected to a GitHub repo (from apps:info) - If yes: clone from GitHub using `gh repo clone ` - If no: use `heroku git:clone --app ` to pull the source from Heroku - Push the code to my GitHub account if it's not already there (use `gh repo create` + `git push`) ## Step 3: Provision the DO managed database FIRST Before building the app spec, create the managed database separately: - Use `doctl databases create` to create a managed Postgres (or Redis/MySQL) cluster in the same region as the app - Do NOT use the `db-s-dev-database` size. Even for testing, these embedded databases lack the disk/RAM required for standard `pg_restore` operations. Use a proper managed database size like `db-s-1vcpu-1gb` or larger. - Wait for the database to be ready, then get its connection details using `doctl databases connection ` - Note the cluster name, database name, host, port, and credentials — you'll need these for the app spec ## Step 4: Build the DO app spec Based on everything discovered above: - Map Procfile processes to DO components (web → Service, worker → Worker, release → Pre-Deploy Job, clock/scheduled → Scheduled Job) - Map Heroku dyno sizes to appropriate DO instance sizes - Map all config vars to environment variables (mark sensitive ones like API keys, secrets, and passwords as encrypted) - Map Heroku add-ons to DO equivalents or note which third-party add-ons can stay as-is by keeping their env vars - Reference the managed database created in Step 3 using `cluster_name` and `db_name` in the database component of the app spec, rather than letting App Platform create an embedded dev database - Create the app-spec.yaml file ## Step 5: Deploy with doctl - `doctl apps create --spec app-spec.yaml` - Poll deployment status every 10 seconds until it succeeds or fails - Show me the live `.ondigitalocean.app` URL when done ## Step 6: Migrate database data After the app is deployed and running: - Confirm which database the app is actually connected to — do NOT assume it's `defaultdb`. The managed DB cluster may contain multiple databases. Use `doctl databases connection ` and `\l` in psql to verify the exact database name before restoring. - `pg_dump` the Heroku database using `--no-owner --no-acl --format=custom` (this is read-only and does not modify Heroku) - `pg_restore` into the correct DO database using `--no-owner --no-acl` - Verify row counts in key tables after restore - Strip Heroku-specific artifacts: `_heroku.*` event triggers and `transaction_timeout` errors during restore are expected and harmless ## Step 7: Functional Verification Provide a checklist of the components deployed (Services, Workers, DBs). Remind me that this is a test environment and that I should verify application logic and connectivity before considering a production DNS cutover. ## Important rules: - Pause after Step 1 and show me what you found before proceeding — let me confirm which app and review the config - Pause again after Step 4 to let me review the app spec before deploying - Do NOT change DNS records — I'll do that after verifying everything works - Flag any Heroku add-ons that don't have a direct DO equivalent so we can discuss alternatives ## DigitalOcean preferences: - Region: - Instance sizing: recommend based on my Heroku dyno types ``` ## Next Steps After you’ve successfully migrated from Heroku to DigitalOcean App Platform, explore some other capabilities of the DigitalOcean platform: - **Autoscaling:** Configure horizontal autoscaling based on CPU or HTTP request metrics to handle traffic spikes automatically. - **Log forwarding:** Stream application logs to Papertrail, Datadog, Logtail, or your own OpenSearch cluster. - **Alerts and monitoring:** Set up alert policies for deployment failures, resource usage thresholds, and health check failures. - **Rollbacks:** Instantly roll back to a previous deployment if issues arise. - **App Spec in CI/CD:** Commit your app spec YAML to your repo and use doctl in your CI pipeline for GitOps-style deployments. - **Managed Kubernetes:** When you need more control, DigitalOcean Kubernetes (DOKS) provides a natural next step, no vendor change required. - **DigitalOcean MCP Server:** Integrate AI-powered coding assistants like Claude Code directly with App Platform for streamlined development workflows. ## Get Support If you need help with your migration, DigitalOcean offers several support channels: - **Support tickets:** Available to all users from the Control Panel. [Contact support](https://cloudsupport.digitalocean.com) to get started. - **Community Q&A:** Ask questions and find answers at [DigitalOcean Community Q&A](https://www.digitalocean.com/community/questions). - **App Platform Documentation:** More guides are available in the [App Platform section](https://docs.digitalocean.com/products/app-platform/index.html.md).