background-shape
Docker Compose vs Kubernetes for Local Development
July 27, 2022 · 5 min read · by Muhammad Amal programming

TL;DR — Compose for daily dev: fast, simple, sufficient for 95% of services. Local k8s (k3d, Kind, minikube) when you need true production parity, are testing k8s-specific features, or your CI deploys via Helm/kustomize. Many teams use both: Compose for app dev, local k8s for infra changes.

After Compose for CI, the question every team eventually asks: should we just use Kubernetes locally too? The answer is “usually no, sometimes yes.” This post is the trade-offs.

What each one is

Docker Compose: local orchestrator. Brings up containers, networks, volumes. Simple YAML. Single-machine.

Local Kubernetes:

  • k3d — k3s in Docker containers. Fast startup, light.
  • Kind — Kubernetes in Docker. Official kubernetes-sigs project.
  • minikube — Full k8s in a VM. Older, slower, more features.
  • Docker Desktop’s built-in — single-node k8s baked into Docker Desktop.

All three give you kubectl-accessible local k8s.

What Compose does better

Boot time. 3-10 seconds for a typical stack. Local k8s is 30s-2min depending on what you spin up.

Simplicity. One YAML file, two commands (up, down). Anyone reads it in 5 minutes.

Resource cost. A Compose stack uses what its containers need. A k8s cluster has overhead (kube-apiserver, etcd, controllers, kube-proxy) before you deploy anything.

Dev loop. Change a file → docker compose up --build → 5 seconds to running. With k8s you build image, push to local registry, kubectl apply, wait for rollout — minutes.

Mac compatibility. Compose is first-class on Docker Desktop Mac. k3d/Kind work but are slower (VM layer).

What local Kubernetes does better

Production parity. If your prod runs on k8s with Ingress, ServiceMonitor, NetworkPolicy, ConfigMap, Secret — Compose can’t represent those. Local k8s can.

Manifests are the artifact. k8s manifests (Helm charts, kustomize) are what gets deployed to prod. If you can run them locally, you’re testing the actual artifact, not a Compose approximation.

Multi-pod patterns. Replicas, rolling updates, init containers, sidecars. Compose has lightweight equivalents; not the same.

Operators / CRDs. If your stack uses operators (Postgres Operator, Strimzi for Kafka, Prometheus Operator), only k8s supports them.

Service mesh experimentation. Istio, Linkerd, etc. — k8s-native.

Boot time comparison

For a stack with Postgres + Redis + 3 app services + ingress, measured cold start:

Platform Cold start
Compose 12s
Docker Desktop k8s 95s
k3d (4 services) 45s
Kind 90s
minikube 2m

Compose wins by ~5×. Reality matters: most dev cycles are warm rebuilds, where the gap narrows. But cold-machine boot is real on M1 Macs and CI runners.

Decision matrix

Need k8s-specific features (Ingress, CRDs, operators)?
├─ Yes → local k8s (k3d or Kind)
└─ No
   └─ Production runs on k8s AND deploys via manifests?
      ├─ Yes → consider local k8s for "rehearse the deploy"
      │       OR Compose for daily, local k8s in CI
      └─ No → Compose

For most teams shipping a typical SaaS:

  • Daily app dev: Compose
  • Deploy testing: local k8s on-demand (make k8s-up once a week to verify)
  • CI integration tests: Compose (faster, simpler)
  • CI deploy tests: local k8s (validate manifests render correctly)

The hybrid pattern

What we do at my current company:

  1. Compose stack lives in the repo root. Daily dev runs on this. Fast feedback.
  2. Helm chart lives in a deploy/ directory. Used by CI/CD to push to staging and prod.
  3. Local k8s is make k8s-up — spins up k3d, applies helm chart, port-forwards. Used when working on the helm chart itself or testing k8s-specific behavior (Ingress paths, NetworkPolicy, etc.).

Engineers spend 95% of time in Compose mode, 5% in local k8s mode. Both work; both are tested.

Tools that bridge

A few tools that translate between worlds:

  • kompose — converts compose files to k8s manifests. Useful for migration; rarely for ongoing dev.
  • Skaffold — k8s dev loop tool. Watches source, builds image, deploys to local k8s. Hybrid of “compose dev loop on k8s.”
  • Tilt — similar to Skaffold; more dev-ergonomic. Good for teams committed to k8s for everything.
  • DevSpace — yet another. All compete for the “k8s dev loop” niche.

If you’re in the local k8s for daily dev camp, these tools are essential. They reduce the build-push-apply-wait cycle to seconds.

What’s NOT a reason to use local k8s

“It’s how prod runs.” Compose can mirror prod enough for most code changes. You don’t rebuild a copy of prod to test a one-line change.

“Kubernetes is the future.” Maybe. Doesn’t mean your dev loop should suffer.

“We need to learn k8s.” Use the staging cluster. Run kubectl against the real thing in a controlled namespace.

“It’s more ‘correct’.” No such thing. Right tool for the job.

What IS a reason

  • You ship k8s manifests as your deploy artifact, and they get complex (Helm charts > 200 lines, multiple operators)
  • Your services explicitly use k8s APIs (sidecar injection, downward API)
  • You’re testing custom controllers / operators
  • You’re a platform engineering team working on the cluster itself
  • Production issues frequently turn out to be “we couldn’t have caught this without k8s”

Common Pitfalls

Maintaining both forever. Compose and Helm chart drift apart. Pick which one is canonical; generate or document the other.

Local k8s with no Ingress. Then it’s not really mirroring prod. Set up nginx-ingress or Traefik for parity.

Skipping Compose because “we’re a k8s shop.” Loses simplicity. Compose for dev doesn’t require running prod on Compose.

Forcing junior engineers into k8s for daily work. Slow feedback. Steep learning curve. Compose is gentler.

Treating Compose’s lifecycle as “real” k8s lifecycle. Compose doesn’t have replicas, pods, deployments. The mental model is different.

What I’d recommend

For a team starting fresh in 2022 with a typical SaaS backend deploying to k8s:

  1. Compose for daily dev. One file, simple.
  2. Helm chart for the deploy artifact, committed to repo.
  3. A make k8s-up target that uses k3d + Helm to spin up the chart locally. Used weekly or when working on infra.
  4. CI runs Compose for integration tests. Separately, CI runs helm template + dry-run to validate the chart.

Two artifacts; two purposes; both tested. Compose for speed; k8s for parity.

Wrapping Up

Compose for daily dev; local k8s when you need parity or are working on k8s-specific stuff. Don’t pick one religiously. Friday: the July retro closing out the month.