I write a lot about Kubernetes. I run it daily. I genuinely like it.
So it might surprise you that I spend a fair amount of time talking people out of it.
Here is the reality I keep walking into. A small team, a single product, a roadmap full of real features to build, and someone has decided the first milestone is a Kubernetes cluster. Three nodes minimum, etcd, a CNI, an ingress controller, cert-manager, a monitoring stack. Weeks of work before a single customer sees anything. Everyone nods along, because this is just how serious infrastructure looks in 2026.
I want to show you the other side of that window. Because for a lot of teams there is a version of this where you ship in an afternoon, understand every moving part, and still sleep fine at night.
This post is for anyone standing at that decision. Not to scare you off Kubernetes, but to help you choose it on purpose instead of by default.
The Kubernetes hype
Kubernetes won. It is the default answer for container orchestration, every cloud sells a managed flavour, and every DevOps job ad lists it. When something becomes the obvious choice, people stop asking whether it is the right one. The tool stops being a decision and becomes the water you swim in.
I get the appeal. I picked Kubernetes for my own homelab on purpose, and I have written plenty about why. But “everyone runs it” is the weakest possible reason to run anything, especially infrastructure you have to understand when it breaks at 2am.
Kubernetes earns its complexity by solving a specific set of problems:
- Orchestrating containers across multiple nodes
- Automatically scaling based on load
- Self-healing when containers crash
- Declarative infrastructure configuration
- Service discovery and load balancing
Read that list and be honest about which ones you actually have. If you have all five, Kubernetes is a gift. If you have none of them, it is pure cost with nothing on the other side of the ledger. You are paying for capability you will never use, in a currency you can least afford: your own attention.
When Kubernetes is overkill
You have one application
One monolith. One database. Maybe a Redis cache. It runs fine on a single server today.
What is Kubernetes buying you here?
Your application → Kubernetes cluster → 3+ nodes → etcd →
control plane → ingress controller → cert-manager →
monitoring stack → ...
Versus:
Your application → Docker Compose → 1 server
Same functionality on both sides. The top one costs you roughly ten times the time, money, and cognitive load to get there. The bottom one you can hold in your head.
Your team is small
Kubernetes asks you to understand a lot before you can run it safely. Not just the core, but everything around it:
- Networking (CNI, service mesh, ingress)
- Storage (CSI, persistent volumes)
- Security (RBAC, network policies, pod security)
- Observability (metrics, logs, traces)
- GitOps (ArgoCD, Flux)
A team of two or three developers who also carry the pager does not have the spare hours to learn and maintain all of that on top of building the actual product. Something has to give, and it is usually either your weekends or your uptime.
Your traffic is predictable
The headline feature is autoscaling. But if your traffic sits at a steady 100 requests per second with no nasty spikes, autoscaling solves a problem you do not have. Overprovision a little, set a reminder to revisit in six months, move on.
Your budget is limited
Managed Kubernetes (EKS, GKE, AKS) is not free, and the line items add up fast:
- Control plane fees (~$70-150/month)
- Minimum 3 worker nodes for HA
- Load balancer costs
- Persistent volume costs
- Egress costs
For one simple app you are quickly at $300-500 a month. That same app runs happily on a $20 VPS. The difference is not capability, it is the toll you pay for an abstraction you are not using.
You don’t have microservices
Kubernetes was built for microservices: many small, independent services that scale and fail on their own schedules. A monolith does not have those needs. Running one on Kubernetes is like buying a Ferrari for the weekly grocery run. It works, but every feature you are paying for sits idle in the driveway.
The hidden costs of Kubernetes
The sticker price is the easy part. The costs that actually hurt are the ones that do not show up on the invoice.
Operational overhead
Even on managed Kubernetes, the cluster is a living thing you tend forever:
- Cluster upgrades (new version every 3-4 months)
- Node pool management
- Capacity planning
- Incident response when pods don’t start
- Debugging networking issues
This is steady, recurring work, and someone on the team owns it whether you planned for that or not.
Learning curve
Here is the vocabulary you need before you can reason about a running cluster:
- Pods, Deployments, StatefulSets, DaemonSets
- Services, Ingress, NetworkPolicies
- ConfigMaps, Secrets
- PersistentVolumes, StorageClasses
- RBAC, ServiceAccounts
- Helm, Kustomize
- CRDs, Operators
That is before the ecosystem tools every real cluster grows: Prometheus, Grafana, ArgoCD, cert-manager, external-dns, and a dozen more. Each one is another thing to learn, patch, and understand when it misbehaves.
YAML hell
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app:v1.0.0
ports:
- containerPort: 8080
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 3
---
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
# ... even more YAML
That is the minimum to get one application running. Here is the same intent in Docker Compose:
services:
my-app:
image: my-app:v1.0.0
ports:
- "80:8080"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
Debugging complexity
Container won’t start? On Docker the whole investigation is one line:
docker logs my-app
On Kubernetes the same question turns into a small expedition:
kubectl describe pod my-app-7d9f8b6c5d-x2k9p
kubectl logs my-app-7d9f8b6c5d-x2k9p
kubectl logs my-app-7d9f8b6c5d-x2k9p --previous
kubectl get events --field-selector involvedObject.name=my-app-7d9f8b6c5d-x2k9p
And the answer turns out to be an ImagePullBackOff from a typo in the image name. Or a resource limit. Or a full node. Every layer Kubernetes adds is a layer you traverse when you are trying to figure out why nothing happened. That tax is invisible on a good day and brutal on a bad one.
Alternatives to Kubernetes
So if not Kubernetes, then what? This is the part of the window I actually want you to look through. None of these are toys. Each one is a tool I would reach for in the right situation, and several of them run real production traffic for real companies right now.
Docker Compose
When: One server, multiple containers, simple setup.
services:
app:
image: my-app:latest
ports:
- "80:8080"
depends_on:
- db
- redis
environment:
- DATABASE_URL=postgres://db:5432/app
restart: unless-stopped
db:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:7
restart: unless-stopped
volumes:
postgres_data:
docker compose up -d and you are running. Updates are docker compose pull && docker compose up -d. The entire mental model fits in one file you can read top to bottom.
Pros:
- Simple to understand
- No cluster overhead
- Works anywhere Docker runs
- Perfect developer experience
Cons:
- No automatic scaling
- No high availability (single node)
- Limited orchestration
Docker Swarm
When: Need multiple nodes, but Kubernetes is too complex.
Docker Swarm is the forgotten sibling. Simpler, fewer features, and often more than enough.
# Init swarm
docker swarm init
# Deploy stack
docker stack deploy -c docker-compose.yml myapp
# Scale
docker service scale myapp_web=5
Pros:
- Same Docker Compose files
- Built into Docker
- Easy setup
- Multi-node support
Cons:
- Smaller ecosystem
- Limited community (Docker focuses on Kubernetes)
- Fewer features than Kubernetes
HashiCorp Nomad
When: You want orchestration without Kubernetes complexity, or you run more than just containers.
Nomad is a lightweight alternative that orchestrates containers, VMs, and standalone executables from a single binary. If you run more than just containers, that breadth is worth a serious look.
job "my-app" {
datacenters = ["dc1"]
group "web" {
count = 3
task "app" {
driver = "docker"
config {
image = "my-app:latest"
ports = ["http"]
}
resources {
cpu = 500
memory = 256
}
}
}
}
Pros:
- Simpler than Kubernetes
- Can do more than containers (raw exec, Java, QEMU)
- Good integration with other HashiCorp tools (Consul, Vault)
- Single binary, easy to deploy
Cons:
- Smaller ecosystem
- Fewer managed cloud options
- Less “standard” (fewer resources, tutorials)
Platform-as-a-Service
When: You just want to deploy code, not manage infrastructure.
- Render: Simple deploys, managed PostgreSQL
- Railway: Developer-friendly, good free tier
- Fly.io: Edge deployment, good performance
- Heroku: The original, still solid
# Fly.io example
fly launch
fly deploy
No YAML. No clusters. No ops. The trade is real though, and I will not pretend otherwise: you are handing control to someone else’s platform, which cuts straight against the sovereignty I usually argue for. For a side project or an early-stage product that needs to ship, that can be a fair deal. Just go in with your eyes open about who owns the off switch.
Pros:
- Zero infrastructure management
- Push code, app runs
- Often cheaper for small projects
Cons:
- Vendor lock-in
- Less control
- Can get expensive at scale
Plain VMs
When: You don’t need containers.
I mean it. Not everything has to be a container. A boring server with a service manager has kept things online for decades, and it is about as inspectable as infrastructure gets.
A Python app with systemd:
[Unit]
Description=My App
After=network.target
[Service]
User=app
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/venv/bin/python app.py
Restart=always
[Install]
WantedBy=multi-user.target
Ansible for configuration management. No Docker, no Kubernetes.
Pros:
- Simple
- No container overhead
- Familiar to most engineers
- Debugging is easy
Cons:
- No isolation like containers
- Dependency management harder
- Less reproducible
Serverless / FaaS
When: Event-driven workloads, variable load, don’t want to manage servers.
- AWS Lambda
- Google Cloud Functions
- Azure Functions
- Cloudflare Workers
// AWS Lambda
export const handler = async (event) => {
return {
statusCode: 200,
body: JSON.stringify({ message: "Hello!" })
};
};
Pros:
- Pay per execution
- Automatic scaling to zero
- No server management
Cons:
- Cold starts
- Vendor lock-in
- Difficult for long-running processes
- Debugging is complex
Decision framework
So how do you actually choose, instead of defaulting? Five questions get me most of the way there.
1. How many services do I have?
- 1-3 services: Docker Compose or PaaS
- 4-10 services: Swarm, Nomad, or Kubernetes
- 10+ services: Kubernetes becomes more relevant
2. Do I need dynamic scaling?
- No: Almost anything except Kubernetes
- Yes, but predictable: Scheduled scaling, no K8s needed
- Yes, unpredictable: Kubernetes or serverless
3. What’s my team’s expertise?
- No Kubernetes experience: Don’t start with Kubernetes
- Some experience: Managed Kubernetes (EKS, GKE)
- Lots of experience: Whatever you want
4. What’s my budget?
- Small (<$100/month): VPS, PaaS, or serverless
- Medium ($100-1000/month): Depends on requirements
- Large (>$1000/month): Kubernetes becomes economically viable
5. Do I need multi-cloud or hybrid?
- No: Use what the cloud offers
- Yes: Kubernetes is the best option for portability
When Kubernetes IS the right choice
I have spent this whole post arguing for restraint, so let me be just as clear about the other side. When the problems are real, Kubernetes is genuinely the best tool I know, and I reach for it without hesitation:
- Many microservices that scale independently
- Team with Kubernetes expertise that can maintain it
- Complex deployment requirements (canary, blue-green)
- Multi-tenant platform for multiple teams
- Portability between clouds, which is exactly why I run it in my own homelab. The same manifests move from a refurbished mini PC to a cloud and back, and that freedom is worth real complexity to me.
- Ecosystem (Prometheus, ArgoCD, and friends) that you will actually use
If a few of those describe you, Kubernetes is probably the right call, and everything before this section was about helping you arrive here on purpose.
Why we pick it anyway
Here is the honest obstacle, the thing that keeps teams reaching for Kubernetes when something smaller would do. Kubernetes is the safe answer in a meeting. Nobody gets second-guessed for choosing the industry standard. It looks good on the architecture diagram and good on a CV. Choosing Docker Compose for a production app feels like admitting you are not serious, even when it is the smarter engineering call.
I think that gets it backwards. Picking the heaviest tool to look credible is the opposite of understanding your own system. The credible move is matching the tool to the problem and being able to explain exactly why.
My rule of thumb
Start simple. Add complexity when you need it, not when you think you might need it someday.
Docker Compose for development and small production workloads. Kubernetes when you actually have the problems Kubernetes solves, and not a sprint before.
Saying “we don’t need Kubernetes” out loud is a sign of a team that understands its own system. That is the position you want to argue from.
Wrapping up
Before you adopt Kubernetes, sit with three questions:
- What problem am I solving?
- Is Kubernetes the simplest solution to that problem?
- Do I have the time, money, and expertise to keep it healthy?
A “no” to any of those is a nudge to look at the alternatives. Docker Compose, Nomad, PaaS, plain VMs all exist because they each fit a shape of problem better than Kubernetes does.
You do not have to bet the whole company to find out. Run the next internal tool on the simpler stack. Measure setup time, build time, and how long it takes a new teammate to ship their first change. Let the numbers argue. My bet is the same one I make in my own infrastructure: the best system solves your problem with the least complexity you can get away with, and stays understandable when you are tired and it is late. Sometimes that is Kubernetes. More often than the job ads suggest, it is not.
Choose on purpose.
