I have several unpopular opinions about software architecture. The most controversial: you don’t need microservices. For the vast majority of use cases, a well-built monolith is not just good enough — it’s better. I get it — for microservice devotees, that’s already enough reason to drop this and leave. But if you’re an engineer, the least you can do is follow the math before you follow the crowd.
Let’s not talk about your average B2B SaaS with 10,000 users. Let’s go straight to the extremes. IoT fleets. Ad bidding platforms. Financial trading systems. The kind of workloads that engineers use to justify distributed architectures in the first place.
Before we even talk about a full monolith, consider a simpler thought experiment: multiple services, same machine, communicating through Unix sockets. No network, no broker, no distributed anything — just processes talking through the kernel. Here’s what the numbers look like:
A Unix socket — which is just two processes talking through the kernel on the same machine — delivers 5 to 20 GB/s with microsecond latency. Kafka, the darling of distributed systems, tops out at 1–2 GB/s on a good day, with millisecond latency at best.
That’s not a marginal difference. That’s a 10x gap in throughput and a 1000x gap in latency, and you’re paying for it with broker clusters, Zookeeper (or KRaft, if you’re lucky), schema registries, consumer group rebalancing, and a dedicated platform team to keep the whole thing alive.
We haven’t even talked about the monolith yet. Because Unix sockets, fast as they are, still go through the kernel. A monolith doesn’t. Inside a single process, communication is just a function call.
Think carefully before trading a function call for a network call.
I’ve been running this exact approach in production on a side project — Serija, a B2B SaaS for the real estate market. The entire thing lives on a single VPS, deployed through Kamal. One tool handles the reverse proxy, pulls secrets from Bitwarden Secrets Manager, builds and runs the Dockerfile, sets up Redis. Everything. One YAML file, under 200 lines.
That’s the full deployment pipeline — production-ready, no DevOps team required.
Before we talk about scaling out, let’s talk about scaling up — because I suspect most engineers have never actually priced it.
On Hetzner, $200/month gets you an AMD EPYC 9454P server. 96 cores, up to 256 GB of RAM, NVMe SSDs, 1 Gbit/s uplink. Not a toy. Not a staging environment. A machine that, running a well-built monolith, handles roughly:
Concurrent web requests → 50,000–200,000/s
PostgreSQL queries → 100,000–500,000/s
In-process events → millions/s
Active WebSocket sessions → hundreds of thousandsNow let’s see what $200/month buys you on AWS. You get a c6g.4xlarge — 16 vCPUs, 32 GB RAM. Before you’ve paid a cent for RDS, ElastiCache, an Application Load Balancer, NAT Gateway, data transfer, CloudWatch, the hidden egress tax.
It wasn’t benchmarks. It wasn’t papers. It was money. And incentives. And the fact that distributed complexity is extraordinarily good for business — just not your business.
Think about who was loudest in pushing microservices adoption. Cloud vendors, who bill you for every moving part. Conferences, where simplicity doesn’t get you on stage. And they even invented an entire career for it — the Developer Advocate, a role whose entire purpose is to generate enthusiasm for a platform, paid for by the company selling that platform. A salesperson with a GitHub account.
The result is an industry that spent a decade confusing architectural fashion with engineering judgment. Microservices didn’t win because the numbers supported them. They won because every financial incentive in the ecosystem pointed toward them — and nobody with a platform was getting paid to tell you otherwise.