Docker Compose vs Kubernetes: When to Use Which
A pragmatic comparison of Docker Compose and Kubernetes covering scope, operational cost, and the signals that tell you it is time to graduate.
What you'll learn
- ✓What each tool actually does and does not do
- ✓How to compare them on scope and operational cost
- ✓Signals that you have outgrown Compose
- ✓How a Compose file maps to Kubernetes manifests
- ✓Hybrid workflows that use both
Prerequisites
- •Some exposure to Docker containers
What and Why
Docker Compose and Kubernetes are often mentioned in the same sentence, which is misleading. They solve overlapping problems at very different scales. Compose is a single-host developer convenience. Kubernetes is a distributed cluster orchestrator. Choosing between them is mostly about scope, team size, and operational appetite — not a “newer is better” argument.
Mental Model
Compose models one machine. It reads a compose.yaml, starts a handful of containers on the same Docker engine, wires them on a private bridge network, and mounts a few volumes. When the machine reboots, you run docker compose up again.
Kubernetes models a fleet. It schedules pods across many nodes, restarts failed ones, rolls out new versions gradually, balances load, manages secrets, and exposes a declarative API for everything. The price is meaningful operational complexity: control plane, etcd, CNI, ingress controllers, RBAC, and a steep learning curve.
Compose:
+--------- one host ---------+
| [web] [api] [db] |
| docker compose up |
+----------------------------+
Kubernetes:
+-- control plane --+
| api / etcd / sched|
+--------+----------+
|
+-------+-------+--------+
| | | |
[node1] [node2] [node3] [nodeN]
pods pods pods pods Hands-on Example
A Compose file for a small web stack:
services:
web:
image: myorg/web:1.0
ports:
- "8080:80"
depends_on:
- api
api:
image: myorg/api:1.0
environment:
DB_URL: postgres://db:5432/app
depends_on:
- db
db:
image: postgres:16
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:
docker compose up -d gives you a working environment in seconds. Perfect for local development, integration tests in CI, or a single-box internal tool.
The Kubernetes equivalent for just the API service looks like this:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
selector:
matchLabels: { app: api }
template:
metadata:
labels: { app: api }
spec:
containers:
- name: api
image: myorg/api:1.0
env:
- name: DB_URL
value: postgres://db:5432/app
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector: { app: api }
ports:
- port: 80
targetPort: 8080
You will write similar manifests for web and db (or use a StatefulSet for the database), plus an Ingress for external traffic. That is roughly 5x the YAML for the same surface, but you also get rolling updates, self-healing, horizontal scaling, and zero-downtime deploys for free.
Common Pitfalls
Using Compose in production at scale. Compose has no built-in scheduler, no health-based rescheduling, no rolling updates. If your single host dies, your app dies.
Reaching for Kubernetes too early. A two-person team running three services on one VM does not need a managed control plane. The complexity tax shows up as on-call pain, mysterious networking bugs, and certificate renewals you do not understand.
Treating kompose convert as a migration plan. The tool generates manifests from a Compose file, but the result rarely follows Kubernetes best practices. Use it as a starting point, not a finished product.
Forgetting state. Compose volumes live on one disk. Kubernetes PersistentVolumes need a storage class, a provisioner, and a backup story.
Practical Tips
Use Compose for local development even if you deploy to Kubernetes. A Compose file that mirrors your production services keeps onboarding fast and lets developers run integration tests without a cluster.
When you outgrow Compose, the signals are clear:
- You need more than one machine to handle load.
- You need zero-downtime deploys.
- You need different team members to deploy independently to shared infrastructure.
- You need automated failover.
If only one or two of those apply, consider intermediate options: Docker Swarm, Nomad, ECS, or a managed PaaS like Fly.io or Render. They cover 80 percent of the orchestration story with 20 percent of the complexity.
When you do adopt Kubernetes, lean on a managed control plane (EKS, GKE, AKS) and a Helm-based deployment workflow. Do not run etcd yourself unless you enjoy 3 a.m. pages.
Wrap-up
Compose and Kubernetes are not competitors; they are tools at different scales. Compose excels at single-host simplicity, which is exactly what most local development and small internal tools need. Kubernetes excels at multi-host reliability, which is exactly what large production systems need. The right answer is almost always “Compose locally, something Kubernetes-shaped in production” — but only when you actually need the something-shaped part. Match the tool to your scale, not to the conference talk you watched last week.
Related articles
- Docker Docker Swarm vs Kubernetes: Which Orchestrator Fits You?
A practical comparison of Docker Swarm and Kubernetes. Understand their mental models, where each shines, common pitfalls, and how to choose for your team.
- DevOps Docker Compose Tutorial: Run Multi-Service Apps Locally
A practical Docker Compose tutorial. Define services, networks, and volumes in one YAML file, then start and stop a full local stack with a single command.
- Docker Docker Compose Network Aliases Tutorial
Network aliases let containers reach each other under multiple names. Learn how aliases work in Compose, when to use them, and the gotchas to avoid.
- Docker Docker Compose Profiles Tutorial: Optional Services Done Right
Use Docker Compose profiles to start only the services you need. Learn the syntax, common patterns, and pitfalls when mixing profiles with depends_on.