There’s a sentiment I’ve seen in Slack threads, Hacker News comments, and late-night Discord vents more times than I can count:
“I just want something like Heroku.”
I’ve said it myself. And I was wrong about what I meant.
Not wrong that Heroku was good. It was genuinely great, and the people who built it understood something most infrastructure companies still haven’t internalized.
But when Salesforce killed the free tier in 2022 (and recently when it announced the end of sale for new enterprise customers) and the migration scramble started, something interesting happened: almost everyone reached for the wrong lesson.
They thought the thing people loved about Heroku was easy deploys. So they built easier deploys.
But that wasn’t it.
What Heroku Actually Did
When I was running a side project on Heroku sometime around 2019 or so, I didn’t think about infrastructure at all. I pushed to Git, it deployed. My Postgres was there. If I needed a queue, I added a plugin. Everything lived in the same mental model, and that mental model took up approximately zero space in my head on a normal workday.
The thing Heroku sold wasn’t a deployment pipeline. It was cognitive zero. The absence of infrastructure as a thing you had to think about.
That distinction matters more than it sounds. Because when you have to think about infrastructure, even a little, it bleeds into everything.
You make product decisions based on what’s easy to deploy, not what’s right. You delay shipping because you’re not sure how the pieces connect. You spend a Friday afternoon debugging why your app can’t reach its own database across two vendor networks instead of building the feature that would’ve landed three new customers.
Heroku removed all of that. One platform, everything pre-wired, one bill, one place to look when something breaks.
The Alternatives Got One Thing Right and Missed the Rest of the Point
After Heroku’s free tier died, Render, Railway, and Fly.io stepped up. All three are genuinely better than wrangling EC2 yourself. I’ve used all of them. I have no interest in being unfair to any of them.
But here’s what actually happens when you use them.
You deploy your app to Render. Then you provision a Postgres database, also on Render, and paste the connection string into your environment variables.
Then you realize you need background jobs, so you sign up for Upstash or add a Redis instance somewhere. Then you want object storage, so you create an S3 bucket on AWS because Render doesn’t have that.
Now you have three dashboards, three billing relationships, three sets of network rules to get right, and the mental overhead of three vendors stitched together with environment variables and prayer.
Railway is closer to the old vision, and it feels more integrated. But it runs on AWS underneath. You’re paying Railway’s margin on top of Amazon’s margin, which means you’re paying the hyperscaler tax twice: once to Amazon for the actual compute, and once to Railway for the privilege of not talking to Amazon directly.
Fly.io made the most interesting bet: they went bare metal. Real hardware, no AWS underneath, which structurally breaks the double-margin problem. But the integration story never quite closed. Your Postgres on Fly is still a thing you wire up separately. The “everything connected” feeling that made Heroku feel magical isn’t there. You’re still the one holding the wires.
The Thing Nobody Talks About: Egress Charges Between Your Own Services
Here’s something that took me longer to fully internalize than it should have.
When your app, your database, and your storage bucket live on different vendors, or even different services inside a cloud provider, data moving between them costs money. Not a lot, usually, until it is a lot.
An app that reads from a database 10,000 times a day, processes some results, and writes to S3 is moving data in three directions constantly. On a hyperscaler-backed platform, some of that movement crosses billing boundaries.
On a platform where compute, Postgres, storage, and queues all live on the same bare metal network? That movement is free, because it never leaves the building.
This isn’t a hypothetical. It’s a structural difference in how the platforms are architected, and it compounds over time in ways that don’t show up clearly on any individual invoice.
“Opinionated” is Doing Real Work Here, Not Marketing Work
I’ve worked with teams that spent actual engineering hours debating which queue system to use. Not implementing it. Debating it. SQS vs. BullMQ vs. RabbitMQ vs. something someone read about on a blog three years ago.
The argument for an opinionated platform isn’t that you’re incapable of making that decision. It’s that the decision doesn’t matter as much as you think, and the time you spend making it is time you’re not spending on the thing that actually differentiates your product.
Postgres is the right database for almost every startup that exists. S3-compatible storage handles almost every file storage use case. A reliable queue is a reliable queue. These aren’t interesting decisions. They stopped being interesting about a decade ago. The interesting decisions are in your product.
An opinionated platform forces you to stop relitigating settled questions. That’s not a limitation. That’s the point.
The Lock-in Question, Answered Honestly
The most common pushback I hear when someone looks at a vertically integrated platform is: “What if I want to leave?”
It’s a fair question and I used to ask it, too. Here’s what I’ve realized: the lock-in concern is almost always theoretical, and it’s usually raised by people who’ve never actually migrated off a platform.
Real lock-in requires something proprietary that your code depends on. A custom SDK that only works with that vendor. A query language that doesn’t exist anywhere else. A deployment model that requires rewriting your app to leave.
If your app runs in a container, uses standard Postgres connection strings, speaks to S3 with an AWS SDK, and publishes jobs to a queue over a standard protocol, you’re not locked in. You’re just deployed somewhere. The migration path is a pg_dump, a bucket copy, and a new docker push. I’ve done migrations like that in a weekend.
The platforms that actually create lock-in are the ones that abstract everything into their own proprietary layer. Serverless functions with custom runtimes. Vendor-specific databases with proprietary query features. Edge compute that only exists on one network. Those are the things worth being suspicious of.
Where Each Alternative Actually Breaks Down
Render is the easiest to recommend and the easiest to outgrow. Deploy a Next.js app, get a managed Postgres, done.
The problem shows up around month three when you need background jobs and object storage. Render doesn’t have either natively.
So you reach for Upstash for queues and AWS S3 for storage. Now you have three dashboards, three billing relationships, and three networks that have to trust each other. The deployment step takes minutes. Everything around it takes the afternoon.
Railway feels more integrated than Render and the DX is genuinely good. But it runs on AWS. That’s not a criticism of the team. It’s a structural fact that has downstream consequences. As I mentioned before, you’re paying Railway’s margin on top of Amazon’s margin, and data moving between your app and your database may cross billing boundaries depending on how Railway has provisioned things. The cost doesn’t look alarming on any single invoice. But it compounds.
Fly.io made the most interesting architectural bet. Real hardware, no hyperscaler underneath, which structurally breaks the double-margin problem. \
I’ve deployed on Fly and the performance at the edge is real. But compute and Postgres are still separate things you wire together. Storage is still an external conversation. The “everything connected” feeling isn’t there because the connections are still yours to make.
What the “I Just Want Something Like Heroku” Crowd Actually Needs
I’ve thought about this a lot, and I think the nostalgia is real but misdirected.
People don’t want Heroku specifically. They want the feeling that infrastructure is someone else’s problem. Not because they can’t handle it, but because handling it isn’t why they got into building software. They want to push code and have things work. They want one place to look when things break. They want a bill they can understand.
The platforms that came after Heroku optimized for the wrong thing. They made the deployment step easier while leaving the integration work on you. They gave you a better on-ramp to the same fragmented landscape.
The more honest path is a platform that’s made the architectural decisions Heroku never fully made: own the hardware, connect the services at the network level, charge one bill, and let the developer focus on code.
That’s not nostalgia. That’s just what the problem actually requires.\
Another Alternative to Heroku
Here is what I’ve been watching: Atlasflow. Bare metal, with compute, Postgres, S3-compatible storage, and queues on the same network before you ever touch them. And one bill.
I haven’t run a production workload there yet, so I can’t speak to reliability under pressure. That matters and I won’t pretend it doesn’t.
But the architectural argument is the most honest attempt I’ve seen at solving the actual problem rather than the symptom. Every other platform made deploy easier. Atlasflow is asking whether the integration should have been your problem in the first place.
That’s not a small distinction. It’s the whole thing.
I’ve been building production apps for close to a decade and belive me, you, the infrastructure landscape has gotten better in almost every measurable way. The integration problem is still mostly unsolved.