For the past decade, the software industry has been swept up in the wave of containerisation, Kubernetes, and microservices. The story has been compelling: break your system into small, independently deployable services, wrap each in a container, orchestrate them with Kubernetes, and you’ll get infinite scalability, resilience, and agility.

But once you’re deep in a real system — watching a “lightweight” Rust service need nearly 1GB of memory just to stay alive inside a cluster — the shine wears off. The truth emerges: microservices and containers come with a very real, often hidden cost that engineers only fully appreciate the hard way.

Containers multiply everything

A monolithic application has one process, one runtime, one scheduler, one connection pool, one logging pipeline, and one set of health checks. When you split that monolith into microservices, you don’t just split the code — you multiply the infrastructure footprint.

Each service now has its own:

  • Runtime — its own language runtime or framework instance.
  • Scheduler — its own async runtime or thread pool.
  • Connection pools — to databases, caches, and message brokers.
  • HTTP server — routing, buffers, and request handling.
  • Health checks — liveness and readiness endpoints.
  • Logging and metrics — separate streams and exporters.
  • Container overhead — cgroups, namespaces, and runtime bookkeeping.

The result is that a service that might use a few hundred megabytes in a monolith can easily demand several times that once containerised and orchestrated.

Kubernetes normalises over-provisioning

Kubernetes encourages you to define resource requests and limits for every container. In theory, this is about fairness and stability. In practice, it often becomes:

  • “Give it 512Mi to be safe.”
  • “Let’s bump it to 1Gi just in case.”
  • “Better to over-provision than get paged at 2am.”

Multiply that mindset across 10–20 services and you’re suddenly running a cluster that needs tens of gigabytes of RAM for workloads that might comfortably fit inside a single 4GB JVM monolith.

Cloud providers don’t complain — more pods mean more nodes, and more nodes mean more compute spend. But for engineering teams, this is where the “real cost” of microservices starts to show up, both technically and financially.

The orchestration layer isn’t free

Kubernetes itself consumes resources before your application even starts. On each node, you’re running components like:

  • kubelet and kube-proxy
  • container runtime and CNI plugins
  • DNS, metrics, and logging agents
  • Optional service meshes and sidecars

In local environments like KIND, all of this is squeezed into a single Docker container pretending to be a cluster. It’s no surprise that a Rust service that looks lean on bare metal suddenly needs a huge memory envelope just to avoid being OOM-killed.

Microservices solve real problems — and create new ones

Microservices aren’t a scam. They genuinely help with isolation, blast-radius control, independent deployability, and domain boundaries. For complex systems — especially in trading, fintech, and telecoms — those properties matter.

But they introduce a new dominant challenge: operational complexity.

  • Distributed debugging — failures span services and hops.
  • Tracing and observability — you need end-to-end visibility.
  • Version skew — different services move at different speeds.
  • Schema and contract drift — APIs and data models evolve independently.
  • Cascading failures — one service’s outage can ripple across the system.

You trade simple deployments and straightforward debugging for a distributed system that demands serious investment in tooling, practices, and discipline.

The case for the modern monolith

The industry is slowly rediscovering that a well-architected monolith is not a relic — it’s a viable, often superior choice for many teams and stages of growth. A modern monolith can still be:

  • Modular — with clear domain boundaries inside one codebase.
  • Scalable — vertically and horizontally where needed.
  • Testable and maintainable — with strong internal contracts.
  • Operationally simple — one deployment, one runtime, one place to debug.

For many organisations, the pragmatic path is not “microservices from day one” but: start with a modular monolith, then split out services only when scale, team structure, or regulatory constraints truly demand it.

Choosing architecture with eyes open

Containerisation and microservices are powerful tools, but they are not free and they are not universally appropriate. Microservices optimise for organisational scalability, not resource efficiency. Containers optimise for portability, not minimal overhead. Kubernetes optimises for orchestration, not simplicity.

The real engineering wisdom is not in blindly choosing monoliths or microservices, but in understanding the trade-offs with brutal honesty and aligning architecture with the reality of your team, your domain, and your stage.

Sometimes, the most “modern” decision you can make is to admit that a single, well-built application running in a few gigabytes of memory is better than a fleet of over-provisioned microservices burning through your infrastructure budget — and your engineers’ time.