The zero-downtime plan
- Deploy your app to AWS, connected to your existing Heroku database.
- Verify it end to end, then switch production traffic to AWS by updating DNS.
- Migrate the database to RDS.
- Shut down the Heroku app.
Terminology
Step 1: Deploy to AWS against the Heroku database
- Connect your AWS account and your Git provider to Ravion.
-
Create a project with a production environment. Pick
us-east-1(oreu-west-1for EU apps) — that is where Heroku runs, and keeping the app in the same region as the Heroku database keeps latency low during the transition. -
Add a network, an ECS cluster, and one
rvn-ecs-webmodule per web process type, plus anrvn-ecs-workerper worker process type. Copy build and start commands from yourProcfile. The framework guides have working configs for Rails, Django, Node, Laravel, and others. -
Export your Heroku config vars and load them into one Secrets Manager secret, then reference them from the modules:
See Managing secrets for the reference format. Leave
DATABASE_URLandREDIS_URLpointing at Heroku for now. -
Allow the AWS app to reach Heroku Postgres. Heroku Postgres is publicly reachable over TLS, so this works from a private subnet through the NAT gateway. If you use Heroku’s IP allowlisting (Private or Shield spaces), allowlist the NAT gateway’s public IP. Pass pre-allocated
nat_gateway_eip_allocation_idsto the network module so that IP stays stable — seervn-aws-network. - Deploy and test on the Ravion-provided URL. Your app is now running on AWS against production data, but serving no production traffic.
Step 2: Switch production traffic
- Set up your domain following Custom domains: request an
rvn-acm-certificate, create the validation record, attach the certificate to the ECS cluster load balancer (or CloudFront). None of this moves traffic yet. - Lower the TTL on your existing DNS records to 60 seconds and wait for the old TTL to expire.
- Save the current Heroku DNS values, then update the records to the Ravion-provided ones.
- Watch error rates and the Ravion logs and metrics tabs. To revert, point DNS back at Heroku.
Step 3: Migrate the database
Production traffic is now served from AWS, but the database is still on Heroku. Follow Migrate Heroku Postgres to RDS to move it, then update theDATABASE_URL key in your environment secret and redeploy.
Do the same for Redis if you use it: create an rvn-elasticache module and update REDIS_URL. Cache data usually does not need to be migrated; queue data (Sidekiq, Bull, Celery) should be drained on Heroku before the switch.
Step 4: Decommission Heroku
Keep the Heroku app and database running for about a week after the database cutover, scaled to the minimum. Then delete them.Application changes to expect
- Commit SHA.
HEROKU_SLUG_COMMITandSOURCE_VERSIONdo not exist. If your app needs the commit at runtime (for Sentry releases, for example), pass it through the pipeline: take the commit as a pipeline input and forward it as a build environment variable or image tag — see Build. - Client IP. Behind an ALB the socket peer address is the load balancer’s, and the ALB appends the client IP it saw to
X-Forwarded-For— so the first entry is client-controlled and spoofable. Take the rightmost entry the ALB added, or configure the framework’s trusted-proxy setting for one hop (Expressapp.set("trust proxy", 1), Railsconfig.action_dispatch.trusted_proxies). With CloudFront in front, useCloudFront-Viewer-Addressor trust two hops. Rate limiters and audit logs are the usual places this bites. - No swap. Heroku dynos swap to disk when memory is exhausted; Fargate tasks are killed. Set
task_memorywith headroom and watch the memory chart after cutover. Node apps also needNODE_OPTIONS=--max-old-space-sizesized to the task — see JavaScript heap out of memory. - Keep-alive timeouts. The ALB idle timeout is 60 seconds, and several frameworks default to less. See Random 502 errors behind a load balancer.
- Postgres over TLS. RDS enforces TLS on PostgreSQL 15+ by default. See PostgreSQL SSL connection error.
- Port. Ravion sets
PORTfrom the module’scontainer_port, the same way Heroku does. - Ephemeral filesystem. Same as Heroku: anything written to disk is lost when the task is replaced. Use S3 (
rvn-s3) or EFS (rvn-efs) for files that must persist.