INFRASTRUCTURE

Replace Heroku with a self-hosted platform (and cut your bill by 80%)

April 2, 2026 · 10 min read

Mikel Martin

CTO, Keni Engineering

Heroku changed the game when it launched. Push code, it runs. No servers to manage, no deploy scripts to write, no infrastructure to think about. For a long time, that simplicity was worth the premium. But in 2026, Heroku pricing has become brutal for growing teams. A single Performance-M dyno costs $250 per month. A comparable VPS with more resources runs $20 to $40 per month. The math no longer works.

This guide walks through the full migration: what you actually lose, what you gain, the exact stack that replaces each Heroku component, and a step-by-step migration path. If your Heroku bill has crossed $500 per month and keeps climbing, this is for you.

The Heroku cost problem

Heroku pricing looks reasonable when you start. A Basic dyno is $7 per month. But the moment you need production-grade resources, the cost curve goes vertical. Let us walk through a typical setup for a growing SaaS application with a small team.

  • 2x Performance-M dynos (web + worker): $500/month. You need at least two for a production app with background jobs.
  • Heroku Postgres Standard 0: $50/month. The free tier maxes out at 10,000 rows, so any real application needs a paid plan. Standard 0 gives you 64 GB and 120 connections.
  • Heroku Redis Premium 0: $15/month. Needed for caching, sessions, or Sidekiq/Celery queues.
  • Heroku Scheduler: Free, but it runs on a dyno that costs $7+ per execution.
  • Papertrail (logging): $11/month for the 10 MB/day plan. You will need more.
  • Scout APM or New Relic: $25 to $99/month for basic monitoring.
  • SSL endpoint: Included now, but Heroku ACM only covers *.herokuapp.com. Custom domains need proper configuration.

Total for this modest setup: $600 to $700 per month. And this is for a single application. Teams running a staging environment, multiple services, or higher-tier database plans easily hit $1,000 to $1,500 per month. We have seen Heroku bills over $3,000 per month for teams with 5 developers running two apps with staging environments.

Now compare that to a self-hosted setup. A Hetzner CX41 server (4 vCPU, 16 GB RAM, 160 GB SSD) costs $15.90 per month. A DigitalOcean droplet with similar specs runs about $48 per month. Even if you go with two servers for redundancy plus a managed database, you are looking at $80 to $150 per month total. That is the same workload at 10 to 20% of the Heroku price.

The gap only widens as you grow. On Heroku, scaling means adding more dynos at $250 each. On a VPS, scaling means upgrading to a bigger server at marginal cost. A server with 8 vCPU and 32 GB RAM at Hetzner costs $30 per month. That is less than one-eighth of a single Performance-M dyno.

What you lose (and what you don't)

Let us be honest about what Heroku gives you and what actually matters once you leave. The fear of leaving Heroku is usually worse than the reality.

What you lose

  • git push deploys: the magical git push heroku main workflow disappears. But CI/CD replaces it with something better: automatic deploys on push to main, with tests running first. You trade one command for zero commands.
  • Managed Postgres: Heroku handles backups, failover, and upgrades for you. On a VPS, you run Postgres in Docker with automated daily backups. It takes 30 minutes to set up and runs reliably for years. For teams that want managed Postgres without Heroku, services like Neon or Supabase offer it at a fraction of the cost.
  • Add-on marketplace: Heroku's add-on ecosystem made it easy to bolt on services. But most popular add-ons have open-source equivalents or cheaper standalone services. Papertrail becomes Loki or just Docker log drivers. New Relic becomes Prometheus plus Grafana.
  • Zero ops: someone needs to maintain the server. Security updates, Docker upgrades, disk space monitoring. This is real work, but it is predictable and can be automated. It takes about 2 to 4 hours per month for a well-configured server.

What you keep

  • Containerized applications: if your app runs on Heroku, it already runs in a container. Docker Compose gives you the same isolation with more control.
  • Environment variables: Docker Compose and CI/CD tools support environment variables natively. The workflow is the same.
  • Automatic TLS: Traefik with Let's Encrypt gives you free automatic HTTPS certificates. No manual renewal, no configuration per domain.
  • Scaling: Docker Compose supports replicas. For most small to mid-size apps, a single server with proper resource allocation handles more traffic than multiple Heroku dynos.

The self-hosted equivalent stack

Every Heroku component has a direct self-hosted equivalent. Here is the stack we use and recommend to clients. It is battle-tested, well-documented, and runs on a single server for most teams.

VPS provider: $20 to $80 per month

Hetzner is the best value for most teams. Their CX41 (4 vCPU, 16 GB RAM) at $15.90 per month handles what would require $500+ in Heroku dynos. DigitalOcean and Vultr are solid alternatives with better US data center coverage. For teams that need US-based servers for latency or compliance, DigitalOcean droplets in NYC or SFO work well at $48 to $96 per month for comparable specs.

Start with a single server. Add a second one only when you genuinely need high availability or your workload outgrows the first. Most apps with under 10,000 daily active users run comfortably on one well-sized server.

Docker Compose for orchestration

Docker Compose replaces Heroku's Procfile and dyno management. Your entire application stack, including web server, worker processes, databases, and cache, is defined in a single docker-compose.yml file. Each service is a container. You start everything with docker compose up -d.

The advantage over Heroku: you control resource allocation. Instead of paying $250 per dyno and hoping Heroku allocates enough memory, you define exactly how much CPU and RAM each container gets. You also get persistent storage, which Heroku's ephemeral filesystem does not provide.

Traefik for routing and TLS

Traefik is a reverse proxy that replaces Heroku's built-in router and SSL termination. It automatically discovers your Docker containers, routes traffic to the right service based on domain names, and provisions Let's Encrypt TLS certificates. No manual configuration per service. Add a few labels to your Docker Compose service and Traefik handles the rest.

Traefik also gives you middleware for rate limiting, basic auth, IP whitelisting, and request headers. Things that require separate Heroku add-ons or buildpacks are built into Traefik.

CI/CD for deployments

GitHub Actions or Woodpecker CI replace Heroku's git push deploys. The workflow: push to main, CI runs tests, builds a Docker image, pushes it to a registry, and tells the server to pull the new image. The whole process takes 2 to 5 minutes and requires zero manual intervention.

This is actually better than Heroku's deploy model. With Heroku, a failed deploy still goes to production unless you set up a separate CI service. With a proper CI/CD pipeline, your tests must pass before deployment even starts. You also get build caching, parallel test execution, and deployment rollbacks, all things that Heroku either does not offer or charges extra for.

GitHub Actions is free for public repos and offers 2,000 minutes per month on the free tier for private repos. For most teams, that is plenty. Woodpecker CI is a self-hosted alternative that runs on your own server with no usage limits.

PostgreSQL with automated backups

PostgreSQL runs in a Docker container with a mounted volume for data persistence. Set up a daily backup job using pg_dump piped to a tool like Restic, which encrypts and uploads backups to S3-compatible storage. Total cost for backup storage: $1 to $5 per month depending on database size.

Heroku Postgres Standard 0 gives you 64 GB and daily backups for $50 per month. A self-hosted Postgres with the same storage and automated backups costs the price of the disk space on your VPS (included) plus $2 per month for backup storage. The performance is often better because you are not sharing resources with other tenants.

Redis in Docker

Redis runs as another container in your Docker Compose file. One line in the configuration. Heroku Redis Premium 0 costs $15 per month for 50 MB. Self-hosted Redis gets whatever memory you allocate from your server, at no additional cost. Most applications need less than 500 MB of Redis, which is trivial on a modern VPS.

Prometheus and Grafana for monitoring

Prometheus scrapes metrics from your containers and server. Grafana displays them in dashboards. Together, they replace Heroku Metrics, Librato, and whatever APM add-on you were paying for. Prometheus collects CPU, memory, request latency, error rates, and custom application metrics. Grafana gives you alerting via email, Slack, or PagerDuty.

The setup takes about an hour. The ongoing cost is zero since both run as Docker containers on your existing server. Compare that to New Relic at $25 to $99 per month or Datadog at $15 per host per month.

The migration path

Migrating from Heroku to a self-hosted platform is not as scary as it sounds. For a typical web application with a database, you can do it in a weekend. Here is the step-by-step path.

Step 1: Containerize your application

If your app runs on Heroku, it already has a Procfile. You need a Dockerfile instead. Most frameworks have well-documented Docker setups. A Rails app, a Django app, or a Node.js app each takes 15 to 30 minutes to containerize properly. The key is to match your Heroku buildpack's runtime version. If Heroku runs your app on Ruby 3.2 with Node 18, your Dockerfile should use the same versions.

Test locally with docker compose up. If the app runs on your laptop, it will run on the server. This is one of Docker's core promises and it actually delivers.

Step 2: Set up CI/CD

Create a GitHub Actions workflow (or Woodpecker pipeline) that builds your Docker image, pushes it to a registry (GitHub Container Registry is free for public repos, $4 per month for private), and deploys to your server via SSH or a deployment tool. The workflow file is typically 30 to 50 lines of YAML. Run your existing test suite as part of the pipeline so you never deploy broken code.

Step 3: Configure the VPS

Provision a VPS, install Docker, set up a firewall, and configure Traefik. This can be done manually in about an hour, or automated with Ansible in 15 minutes if you have playbooks ready. The server needs: Docker and Docker Compose installed, ports 80 and 443 open, SSH key authentication (disable password login), and automatic security updates enabled.

Set up Traefik as your entry point. It listens on ports 80 and 443, handles TLS termination, and routes requests to the right container based on domain names. A basic Traefik configuration is about 20 lines of YAML.

Step 4: Migrate the database

This is the most delicate part. Export your Heroku Postgres database using heroku pg:backups:capture followed by heroku pg:backups:download. Import it into your new Postgres container using pg_restore. For small databases (under 10 GB), this takes minutes. For larger databases, you might want to schedule a maintenance window.

Test the import thoroughly. Run your application against the migrated database and verify that everything works before switching production traffic. A common mistake is forgetting to migrate Heroku-specific extensions or configurations.

Step 5: Update DNS and go live

Point your domain's DNS to the new server's IP address. If you are using Cloudflare, the switch is instant. For other DNS providers, lower the TTL to 300 seconds a day before migration, then make the switch. Keep Heroku running for 24 to 48 hours after the DNS change as a fallback. Once you confirm everything works on the new server, tear down the Heroku app.

The whole process, from Dockerfile creation to DNS switch, can be done in a weekend for a straightforward application. More complex setups with multiple services, queues, and scheduled jobs might take a week of part-time work.

What about Render, Railway, and Fly.io?

Before going fully self-hosted, it is worth considering the modern PaaS alternatives. They have learned from Heroku's mistakes and offer a much better price-to-value ratio.

  • Render: the closest Heroku replacement. Web services start at $7 per month, with a generous free tier. Managed Postgres starts at $7 per month. A comparable setup to our Heroku example runs about $50 to $150 per month. Good developer experience, auto-deploys from Git, built-in TLS. The downside: pricing still scales linearly with resources, and you are locked into their platform.
  • Railway: usage-based pricing starting at $5 per month. Great for small projects and prototypes. You pay for actual CPU and memory usage, which can be cheaper for low-traffic apps but unpredictable for high-traffic ones. The developer experience is excellent, maybe the best of any PaaS.
  • Fly.io: deploys containers to edge locations worldwide. Starts at $0 for small apps. Great for applications that need low latency globally. The pricing model is more complex, and costs can surprise you when traffic spikes. Database hosting (Fly Postgres) is still maturing.

These platforms are a legitimate middle ground. They cost 50 to 70% less than Heroku while keeping the managed experience. But they are still 3 to 5x more expensive than self-hosted infrastructure. For a team spending $150 per month on Render, the savings from going self-hosted might not justify the operational overhead. For a team spending $500+ per month, the math changes significantly.

The other consideration is vendor lock-in. Render, Railway, and Fly.io are all relatively young companies. If one of them shuts down or changes pricing (remember Heroku removing its free tier in 2022?), you need to migrate again. A self-hosted Docker Compose setup runs on any VPS provider. Switching from Hetzner to DigitalOcean takes an afternoon, not a rewrite.

When to stay on Heroku

Self-hosting is not always the right call. Here are the situations where Heroku (or a managed PaaS) still makes sense.

  • Your team has zero ops knowledge: if nobody on the team has ever SSH'd into a server, self-hosting introduces a steep learning curve. A managed PaaS lets you focus on building product. The premium is the cost of not learning infrastructure.
  • You are pre-revenue and time matters more than money: when you are racing to find product-market fit, every hour spent on infrastructure is an hour not spent on the product. Heroku's simplicity has genuine value in this phase. Pay the premium, ship faster, revisit infrastructure when the bill becomes painful.
  • You have one developer and no bandwidth: a solo developer maintaining infrastructure while building features is a recipe for burnout. If you cannot dedicate even 2 to 4 hours per month to server maintenance, a managed platform is the right choice.
  • Compliance requirements: if you need SOC 2, HIPAA, or PCI compliance, Heroku and other managed platforms provide compliance certifications out of the box. Achieving the same on self-hosted infrastructure requires significant additional work.
  • Your bill is under $200 per month: the savings from self-hosting a $200/month Heroku setup might be $100 to $150 per month. That is $1,200 to $1,800 per year. If the migration takes 20 hours of engineering time at $100/hour, it takes over a year to break even. The ROI only becomes compelling when your Heroku bill exceeds $500 per month.

The real cost of self-hosting

Self-hosting is not free. The VPS costs less, but someone needs to maintain it. Here is a realistic accounting of the ongoing effort.

  • Server maintenance: 1 to 2 hours per month. Security updates, Docker upgrades, disk space management. Most of this can be automated.
  • Incident response: 0 to 4 hours per month. Servers crash, disks fill up, certificates expire. Monitoring and alerting catch most issues before they become outages, but you need someone available to respond.
  • Infrastructure improvements: 2 to 4 hours per month initially, decreasing over time. Setting up monitoring dashboards, optimizing backup schedules, tuning performance.

Budget 4 to 8 hours per month for the first 3 months, then 2 to 4 hours per month once everything is stable. At $100/hour fully loaded developer cost, that is $200 to $800 per month in engineering time. Add that to your VPS cost and compare against Heroku. For most teams spending $500+ per month on Heroku, self-hosting still saves $200 to $1,000 per month even after accounting for engineering time.

The alternative: hire a DevOps consultancy to handle the migration and ongoing maintenance. A managed DevOps service typically costs $2,000 to $5,000 per month, which includes the migration, server management, monitoring, and incident response. For teams that want the cost savings of self-hosting without the operational burden, this is the sweet spot.

Making the switch

Heroku served the industry well for over a decade. It proved that developer experience matters and that deployment should be simple. But the industry has caught up. Docker, Traefik, and CI/CD pipelines deliver the same developer experience at a fraction of the cost. The tools are mature, well-documented, and battle-tested by millions of teams.

If your Heroku bill is growing faster than your revenue, it is time to evaluate self-hosting. The migration is less complex than you think, the cost savings are real, and the operational overhead is manageable for any team with basic Docker knowledge.

Want to understand what a self-hosted platform looks like for your team? Learn about our platform engineering approach or explore managed DevOps if you want the savings without the operational work.

Get the DevOps checklist for your stack

We send one practical guide per week. No spam, unsubscribe anytime.

Ready to cut your hosting bill by 80%?

We migrate teams from Heroku to self-hosted platforms with zero downtime.

Let's talk