How We Migrated a Startup from Heroku to AWS in a Weekend
HN Reference: HN discussion on Heroku's pricing changes and migration stories (Sep 2023)
When Heroku eliminated their free tier and raised prices, we helped a startup client migrate their production SaaS to AWS over a weekend. Here's exactly how we did it.
The Situation
- App: Node.js API + React frontend + PostgreSQL + Redis
- Traffic: ~50K requests/day
- Users: ~2,000 active
- Constraint: Zero downtime allowed
- Budget: Keep costs under $200/month (they were paying $350 on Heroku)
The Plan
We broke the migration into phases:
Phase 1: Infrastructure Setup (Saturday Morning)
Set up the AWS infrastructure before touching production:
- ECS Fargate for the API (2 tasks, auto-scaling)
- RDS PostgreSQL (db.t3.micro, single-AZ for cost)
- ElastiCache Redis (cache.t3.micro)
- S3 + CloudFront for the frontend
- ALB for load balancing
Total time: 3 hours. We used Terraform so it was reproducible.
Phase 2: Database Migration (Saturday Afternoon)
The database was the riskiest part. Here's our approach:
- Set up a read replica from Heroku's PostgreSQL to RDS
- Let the replica sync for 4 hours
- Verified data integrity (row counts, spot checks)
- Prepared the cutover script
Phase 3: Application Deployment (Saturday Evening)
- Deployed the app to ECS pointing to the new RDS instance
- Ran the test suite against the new environment
- Fixed environment-specific issues (Heroku config vars → AWS Secrets Manager)
- Verified all integrations (Stripe, SendGrid, etc.)
Phase 4: Cutover (Sunday Morning)
This was the critical moment:
- Put the Heroku app in maintenance mode (5-minute window)
- Ran the final database sync (delta only)
- Switched DNS from Heroku to the ALB
- Verified the new deployment
- Took Heroku app offline
Total downtime: 4 minutes.
The Tools
- Terraform: Infrastructure as code
- pg_dump/pg_restore: Database migration
- AWS CLI: Automation scripts
- Datadog: Monitoring during and after migration
- PagerDuty: Alerting for any issues
What Went Wrong
SSL certificate issues. Heroku managed SSL automatically. On AWS, we had to set up ACM certificates and wait for DNS validation. Lesson: start this process days before the migration.
Environment variables. Heroku's config vars don't map 1:1 to AWS Secrets Manager. We missed two config values and had a 10-minute debugging session.
Redis connection strings. ElastiCache Redis uses VPC-internal endpoints. We had to update every reference to the Redis URL.
The Results
- Cost: $147/month (down from $350)
- Performance: 20% faster response times (ECS + optimized networking)
- Reliability: Auto-scaling handled traffic spikes that would have degraded on Heroku
- Control: Full access to infrastructure, logs, and metrics
Advice for Others
- Practice the migration first. We did a dry run on Friday night. It caught 3 issues we fixed before the real migration.
- Have a rollback plan. We kept the Heroku app ready to reactivate for 72 hours.
- Communicate with users. We sent an email about the planned maintenance window. Users appreciate transparency.
- Monitor aggressively after. We watched dashboards for 48 hours post-migration.
Migrations are scary but manageable with proper planning. The biggest risk isn't technical — it's underestimating the complexity.