Kubernetes vs Docker: What They Actually Do (and When You Need Each)

4 min read

Amnic

Amnic

Kubernetes

Comparisons

Engineering

Table of Contents

Table of Contents

No headings found on page

Docker packages an application and its dependencies into a container that runs on one host. Kubernetes runs containers across a fleet of hosts and keeps them running when things break. 

They are not competitors; most production stacks use both. Use Docker to build images and run containers locally or on a single VM. Use Kubernetes when you have many services, multiple hosts and a real need for self-healing and zero-downtime rollouts. If you are already there, Amnic's Kubernetes cost management is built for the bill that follows.

Kubernetes vs Docker: What They Actually Do (and When You Need Each)

Most "Kubernetes vs Docker" articles treat the two as rival products. They aren't. Docker builds and runs containers on a single machine. Kubernetes schedules containers across a cluster. The real question isn't which one wins, it's which one (or both) your team actually needs right now and what the operational bill looks like once you've picked. Teams running both tend to land on a structured FinOps practice sooner than they planned.

Are Docker and Kubernetes competitors?

The framing of Docker versus Kubernetes is a category error. Docker builds and runs containers on a single host; Kubernetes schedules containers across many. The product that genuinely competed with Kubernetes was Docker Swarm and Swarm lost. Kubernetes is the default orchestrator in production today.

A few quick facts that settle the debate:

  • According to the CNCF Annual Survey 2023, 84% of organizations are using or evaluating Kubernetes in production.

  • Swarm sits in the low single digits of production usage.

  • Every major cloud (AWS, Azure, GCP) ships a managed Kubernetes service. None ship managed Swarm.

  • Helm, operators, service meshes and GitOps tooling target Kubernetes first and most never get a Swarm port.

Think of Docker as the lathe that shapes the part and Kubernetes as the factory floor that decides where each part goes, replaces it when it breaks and ships replacements at 3 a.m. without waking anyone. The factory floor is where most of the cost attribution gets lost, which is its own problem.

What about Docker Swarm?

Swarm was Docker Inc.'s answer to orchestration. It shipped as part of Docker Engine, used the same CLI and was genuinely easier to learn. It still works. Mirantis, which acquired Docker Enterprise in 2019, committed to maintaining it. But the ecosystem voted with its feet. If you pick Swarm today, you pick a smaller, quieter island. That is a fine choice for a small team. It is a poor one if you plan to hire.

So why does the comparison keep coming up?

Because both words show up on the same job descriptions, in the same conference talks and on the same résumés. Newcomers reasonably assume they are alternatives. They are not. They are layers of the same stack.

What Docker actually is

Docker is a platform for building, distributing and running containers on a single host. Its core pieces are:

  • Docker Engine: a daemon plus CLI that runs containers via runc.

  • Docker Hub: a public registry of container images.

  • Docker Compose: a YAML format for running multi-container apps locally.

  • Dockerfile: a declarative build recipe for a container image.

  • BuildKit: the modern image builder with cache mounts, build secrets and parallel layer builds.

Docker did not invent containers. Linux had cgroups and namespaces years before. What Docker did was wrap the primitives in a developer experience anyone could use in an afternoon. That is why it spread. Today most teams use the Dockerfile and the image format more than the daemon itself and that distinction matters for what comes next.

Docker Engine and runc

The Engine is a long-running daemon (dockerd) that pulls images, creates containers and manages their lifecycle. The call stack underneath looks like this:

  • dockerd receives the API call from the CLI.

  • It hands off to containerd, the container supervisor.

  • containerd calls runc, the low-level OCI-compliant runtime.

  • runc sets up the namespaces and cgroups and starts the container process.

So when you type docker run nginx, four processes are involved before nginx starts. It is more layered than people realise and once you start running hundreds of containers, that layering shows up in utilisation numbers.

Dockerfile and BuildKit

A Dockerfile is a sequence of instructions: pick a base image, copy files, install dependencies, set an entrypoint. BuildKit, which became the default builder in Docker 23.0, added:

  • Concurrent stage builds (faster multi-stage Dockerfiles).

  • Remote build cache (share cache across CI runners).

  • Build-time secrets that never land in image layers.

  • Cache mounts for package managers like npm and pip.

If you are still using the legacy builder, you are leaving build-time on the table.

Docker Hub and image distribution

Docker Hub is the default public registry. It hosts official images for Postgres, Redis, nginx and roughly everything else. It also has rate limits, per Docker's pricing page:

  • 100 pulls per six hours for unauthenticated users.

  • 200 pulls per six hours for free authenticated users.

  • Higher tiers for paid plans.

That is why every serious team mirrors images into ECR, Artifact Registry, or Harbor.

Docker Compose

Compose is the unsung hero. One YAML file, docker compose up and you have Postgres, Redis, your API and a worker running together on your laptop. For local development and small production deployments, it is still the right tool.

What Kubernetes actually is

Kubernetes is a container orchestrator. It runs a cluster of machines (nodes) and decides which containers run where, restarts them when they crash, scales them when traffic spikes and exposes them to the network. A cluster has two halves:

  • Control plane (the brain): kube-apiserver, etcd, scheduler, controller-manager.

  • Worker nodes (where workloads run): kubelet, kube-proxy, container runtime.

Containers are grouped into pods, the smallest deployable unit. Kubernetes does not run containers directly. It tells a runtime to do it, through the Container Runtime Interface (CRI). The runtime is usually containerd or CRI-O. 

Kubernetes is declarative: you describe the state you want and controllers reconcile reality toward that state. That model is the whole secret and it is also what makes cluster spend hard to forecast without a tool like Amnic's forecasting.

The control plane

Each control-plane component has one job:

  • kube-apiserver: the front door. Every kubectl command, controller and kubelet talks to it.

  • etcd: a distributed key-value store that holds cluster state.

  • kube-scheduler: decides which node a new pod runs on based on CPU, memory, affinity and taints.

  • controller-manager: runs the reconciliation loops (ReplicaSet, Deployment, Node, Job, plus a few dozen others).

Worker nodes and pods

Each worker node runs:

  • kubelet: talks to the API server and tells the local runtime what to run.

  • kube-proxy: handles service networking and load balancing.

  • A container runtime: containerd or CRI-O, almost always.

A pod wraps one or more containers that share a network namespace and storage. Most pods are single-container. Multi-container pods are for sidecars: log shippers, service mesh proxies, init containers that prep state. Once pods start scaling out, cost allocation across them stops being optional.

Why CRI matters

The Container Runtime Interface is the plug. Kubernetes does not care if you are running containerd, CRI-O, or some other OCI runtime. This is also why dockershim could be removed without breaking the world. We will get to that.

Kubernetes vs Docker: side-by-side comparison

Before the table, one note: half the rows below are not really apples-to-apples, because Docker and Kubernetes operate at different altitudes. The table is useful for orienting newcomers, not for picking a winner.

Dimension

Docker

Kubernetes

Primary purpose

Build and run containers on one host

Orchestrate containers across many hosts

Scope

Single machine

Cluster of machines

Created by

Docker Inc. (originally dotCloud)

Google, donated to CNCF

Released

March 2013

June 2014

Architecture

Daemon plus CLI plus registry

Control plane plus worker nodes

Scaling

Manual (docker run more containers)

Automatic via HPA, VPA, Karpenter

Self-healing

None natively (Compose has restart)

Built in via controllers and probes

Load balancing

None natively (needs Traefik or similar)

Services, Ingress, kube-proxy

Networking

Bridge, host, overlay (Swarm mode)

CNI plugins (Calico, Cilium, Flannel)

Learning curve

A day to get productive

Weeks to get safe, months to get fluent

The learning curve row is the one I would argue with hardest. A junior can ship something on Docker tonight. They cannot ship safely on Kubernetes tonight. That gap drives a lot of the platform decisions you will make and a lot of the recommendations we end up giving teams who adopted too fast.

How Docker and Kubernetes work together

In a typical production pipeline:

  1. You write a Dockerfile.

  2. You build an image with Docker or BuildKit.

  3. You push the image to a registry (ECR, GCR, Docker Hub, Harbor).

  4. You reference the image in a Kubernetes Deployment manifest.

  5. Kubernetes pulls the image onto a node and starts a pod.

  6. A Service exposes the pod inside the cluster (and an Ingress exposes it outside).

The Dockerfile is the build artifact contract. The image is the portable unit. The pod is the runtime unit. The Service is the network unit. Docker owns the first two steps. Kubernetes owns the last two. Neither does the other's job well.

The Dockerfile

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./

RUN npm ci --omit=dev

COPY . .

EXPOSE 3000

USER node

CMD ["node", "server.js"]

The Kubernetes Deployment

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: ghcr.io/acme/api:v1.4.2

          ports:

            - containerPort: 3000

          resources:

            requests:

              cpu: 100m

              memory: 128Mi

            limits:

              cpu: 500m

              memory: 512Mi

          readinessProbe:

            httpGet:

              path: /healthz

              port: 3000

The Dockerfile is six lines and one developer's responsibility. The Deployment is thirty lines and usually owned by a platform team. That asymmetry is the real story of Kubernetes adoption. For teams trying to understand the operational cost behind all that YAML, a proper cost analyzer is the difference between a guess and a plan.

Does Kubernetes still use Docker? The dockershim story

Since Kubernetes 1.24, released in May 2022, Kubernetes no longer uses Docker as its container runtime. It uses containerd or CRI-O through the Container Runtime Interface. Docker-built images still run unchanged on Kubernetes because they conform to the OCI image specification, which both containerd and CRI-O implement.

What actually changed:

  • dockershim, the shim that translated CRI calls into Docker Engine API calls, was deprecated in 1.20 and removed in 1.24.

  • Nodes switched to talking to containerd or CRI-O directly.

  • Docker-built images kept working because they are OCI-compliant.

  • Managed clusters (EKS, GKE, AKS) handled the swap automatically.

  • Self-hosted clusters switched runtimes, usually with kubeadm doing most of the work.

  • Mirantis maintains a cri-dockerd adapter for teams that genuinely need Docker Engine on the node.

The headlines at the time read "Kubernetes deprecates Docker," and most readers heard "my images will not work." That was never true, as documented in the official dockershim removal FAQ. Your Dockerfile is fine. Your image is fine. What changed is the runtime under the hood on the node, which most application developers never touched anyway.

The lesson is that the OCI image and runtime specs are what kept everything portable. Standards work. The cost of that portability shows up later in your invoice, which is where the unit-cost work actually starts.

Production realities: debugging, observability, security

Once you go from Docker to Kubernetes, the operational surface area changes shape. On Docker, docker logs <container> gives you stdout. On Kubernetes, kubectl logs <pod> does the same, but the pod might already be gone. Pods are ephemeral. They get rescheduled, evicted and replaced.

Things that need to go somewhere durable instead of "the host":

  • Logs (Loki, Elasticsearch, Datadog, CloudWatch).

  • Metrics (Prometheus, Datadog, New Relic).

  • Traces (Tempo, Jaeger, Honeycomb).

  • Cost data, because anomaly detection on cost (not just on errors) becomes the new baseline.

You stop thinking about one host and start thinking about a fleet.

Debugging across a cluster

The daily debugging tools:

  • kubectl describe pod: show events, scheduling decisions, recent failures.

  • kubectl exec: drop into a running container.

  • kubectl debug: attach an ephemeral debug container (GA in 1.25) when the pod has no shell.

  • kubectl logs --previous: read logs from the crashed pod's previous incarnation.

When something is wrong, you usually find out from a Prometheus alert before a user complains, which is the whole point. But the failure modes are weirder: DNS flakes, CNI bugs, PVC stuck in pending, image pull backoff because your registry rate-limited you.

Sidecars and the shape of pods

Sidecar containers became a first-class pattern in Kubernetes 1.28 (beta) and GA in 1.29. Before that, sidecars were just regular containers in a pod, which created ordering and lifecycle headaches. The classic use cases:

  • Log shippers (Fluent Bit, Vector).

  • Service mesh proxies (Istio's Envoy, Linkerd's proxy).

  • Secrets fetchers (Vault Agent, External Secrets).

  • Init containers that prep state before the main container starts.

On plain Docker, you would run these as separate containers and wire them by hand.

Security: RBAC vs the Docker socket

Docker has one trust boundary worth caring about: the Docker socket. Anyone who can talk to /var/run/docker.sock can root the host. That is it.

Kubernetes has a much wider security surface and a much larger set of controls:

  • RBAC for what verbs a subject can perform.

  • ServiceAccounts for pod identity.

  • NetworkPolicy for east-west traffic rules.

  • PodSecurity admission for what a pod is allowed to do at runtime.

  • Audit logging for forensics.

You can scope a workload to read one ConfigMap in one namespace. You can also misconfigure all of it and ship a cluster-admin token in a public Helm chart, which happens more often than anyone admits. Kubernetes gives you the tools. It does not give you the discipline.

Upgrade cadence

Kubernetes ships a minor version roughly every four months and supports three at a time, so a version goes end-of-life in about 14 months, per the official release announcement cadence. That cadence is fine if you have a platform team. It is brutal if you do not. Docker Engine ships major releases far less aggressively and you can mostly leave it alone.

When Docker alone is the right call

Docker without Kubernetes is the right answer more often than the internet suggests. Reach for Docker alone when:

  • You have one app on one VM.

  • You are prototyping or building a side project.

  • Your CI just needs to build images and push to a registry.

  • Your local dev environment is docker compose up with Postgres, Redis and your service.

  • You run internal tools, batch jobs, or scheduled scrapers on a single host.

  • Your traffic is predictable and your service count is in the single digits.

Docker on a $20 VPS with a systemd unit that runs docker compose up -d is a perfectly good production setup. I have seen it serve real traffic for years without incident. The trick is being honest about what you are optimising for. If the answer is "I want to ship a side project this weekend," Kubernetes is a tax, not an asset.

When the answer flips to "we have a real cloud bill now," the FinOps Acceleration Kit is a good first read.

When you don't need Kubernetes

If you have outgrown one VM but have not hit cluster-scale, there is a middle tier most articles skip over. Your options:

  • Docker Compose: multi-container apps on a single host with one YAML file.

  • Docker Swarm: simple multi-host orchestration without the K8s learning curve.

  • HashiCorp Nomad: a lighter scheduler that handles containers plus non-container workloads.

  • AWS ECS: managed sweet spot for teams already on AWS who want orchestration without operating a control plane.

  • Fly Machines or Railway: containers-as-a-service that hides the orchestrator entirely.

  • Podman: Docker-compatible CLI, daemonless, rootless by default.

Podman deserves its own mention. alias docker=podman works for most commands and Red Hat ships it as the default container tool on RHEL 8 and later. For local dev and CI, Podman is increasingly the better Docker.

The honest filter: if your service count is in the single digits, your traffic is predictable and your team is small, Kubernetes will cost you more in operational overhead than it gives back in resilience. Real unit economics on per-service cost are how you spot the moment that tradeoff flips.

When you actually need Kubernetes

Reach for Kubernetes when you operate dozens of services across more than one host, need zero-downtime rollouts and have a platform engineer to own it. If you have one app and one VM, Docker Compose is faster to ship and cheaper to run.

The real signals that you need K8s:

  • You ship rolling deployments multiple times a day.

  • You need horizontal pod autoscaling tied to custom metrics.

  • You need pod-level network policy for isolation between services.

  • You have multiple teams shipping to shared infrastructure.

  • You have outgrown what a single host can hold (vertically or in blast-radius terms).

  • Compliance requires audit logging, NetworkPolicy, or namespace-level isolation.

Companies running Kubernetes in production at scale include:

  • Spotify: open-sourced Backstage on top of its internal K8s platform.

  • Shopify: migrated from a Rails monolith on bare metal to K8s on GKE, documented in their engineering blog.

  • Pinterest: runs thousands of services on K8s.

The CNCF Annual Survey reported that 66% of respondents are running Kubernetes in production, up from 58% the year prior. That is the line you cross when the orchestrator stops being optional and where customer stories from teams running K8s at scale start to read very differently.

The platform team question

Kubernetes is not a product you install. It is a platform you operate. The honest cost is one to three full-time platform engineers, depending on cluster count and team size. Their job covers:

  • Ingress, DNS and certificate rotation.

  • RBAC and secrets management.

  • Observability stack (Prometheus, Grafana, log pipelines).

  • Cluster and node upgrades on Kubernetes' four-month cadence.

  • Cost control on the resulting fleet.

If you cannot justify that headcount, you probably cannot justify K8s. Managed offerings (EKS, GKE, AKS) reduce the load but do not eliminate it.

Decision criteria, ranked

  1. More than ~20 services or more than ~5 hosts.

  2. Multiple teams sharing infrastructure.

  3. Hard requirement for zero-downtime deploys.

  4. Autoscaling is tied to non-trivial signals.

  5. Compliance requirements that need NetworkPolicy and audit logging.

Hit three of those and Kubernetes earns its seat. Hit one and you are probably reaching too early.

Docker vs Kubernetes vs Podman

Podman is the comparison that increasingly matters, because for a lot of teams the real question is not "Docker or K8s," it is "do I still need the Docker daemon at all?"

What makes Podman different from Docker:

  • Daemonless: containers run as child processes of the user's shell, not a privileged background service.

  • Rootless by default: closes the Docker-socket attack surface most teams forget about.

  • CLI-compatible: alias docker=podman works for nearly every common command.

  • Kube-aware: podman generate kube spits out a Kubernetes YAML from a running container.

Podman's sibling tools cover the build-and-distribute side:

  • Buildah: builds OCI images without a daemon, with or without a Dockerfile.

  • Skopeo: moves images between registries without pulling them locally.

So where does each fit?

  • Docker: most familiar tool, best ecosystem of GUI clients and tutorials, easiest for onboarding new developers.

  • Podman: better choice on Linux servers where rootless and daemonless are real security wins; already the default on Fedora and RHEL.

  • Kubernetes: uses neither at runtime; talks to containerd or CRI-O directly.

The three tools rarely compete head-on. The interesting friction is Docker versus Podman on developer laptops, where Podman Desktop is closing the gap fast. Whichever you pick on the build side, the runtime spend lives downstream in your cluster, which is where cost dashboards earn their keep.

Cost and operational TCO

The bill for running Kubernetes is bigger than the line item on your cloud invoice. The control plane fee is the visible part:

  • EKS: $0.10 per hour per cluster (~$73 per month).

  • AKS: free for the control plane on the standard tier.

  • GKE Standard: $0.10 per hour per cluster after the first free zonal cluster.

That is the easy math. Check current rates because they shift.

The real cost is everywhere else:

  • Worker nodes: most clusters run at 40 to 60% utilisation. You pay for the headroom that lets the scheduler bin-pack and for surge capacity during deploys.

  • Observability: a Prometheus + Grafana + Loki stack (or a vendor like Datadog) often costs as much as the cluster itself. Datadog's pricing puts container monitoring at $1 per container per month at list.

  • Platform team: two senior engineers at $200K all-in is $400K per year before you've shipped a single application feature on top.

  • Training: the CNCF charges $445 for the CKA exam. Add 40 to 80 hours of study per engineer and you're at $20K to $40K in fully loaded engineering time before anyone is certified.

This is the part where budgeting at the team and service level stops being a nice-to-have.

Docker's TCO is dramatically lower because there is no separate platform layer. A $40 per month VPS, a CI runner and a registry will run a real product for a real number of users. The catch is that you will hit a ceiling and the migration cost out of that setup is real. The question is not which is cheaper today. It is whether you will need to migrate and when. If you are already past that point, book an Amnic demo and we will show you what your cluster is actually costing you per service.

Common anti-patterns

Don't run Kubernetes for 3 containers. Don't run bare Docker for 300. A few more patterns worth naming:

  • Lift-and-shift to K8s without refactoring. Stuffing a stateful monolith into a single pod is technically possible and operationally miserable. K8s rewards small, stateless, restartable workloads. If yours aren't, fix that first.

  • One giant cluster for everything. Prod, staging, dev and the data team all in one namespace soup. Multi-tenancy in Kubernetes is hard. Separate clusters per environment is usually cheaper than the blast radius of one bad ConfigMap.

  • Ignoring resource requests and limits. Pods with no requests get scheduled anywhere and get evicted first. Pods with limits but no requests are the worst of both worlds. Set both, or set neither deliberately.

  • Treating the Docker socket as harmless. Mounting /var/run/docker.sock into a container is root-equivalent. CI runners, monitoring agents and "just for debugging" sidecars do this all the time. It is the most common footgun.

  • Hand-rolling YAML for everything. If you aren't using Helm, Kustomize, or a similar tool by service ten, you're going to copy-paste your way into a bug.

  • Upgrading Kubernetes once a year in a panic. The release cadence punishes you for waiting. Stay within one minor version of latest, always.

  • Treating cluster cost as a fixed line. It isn't. Idle nodes, oversized requests and forgotten dev clusters add up fast. A regular reporting review catches the drift before it becomes a quarter-end fire.

Our take

Most teams adopt Kubernetes one year too early and Docker Compose one year too late. The question is not which technology is better. It is which one matches your operational maturity. Pick the smaller tool you can actually run and graduate when the pain of staying outweighs the pain of moving.

FAQ

What is the main difference between Docker and Kubernetes?

Docker builds and runs containers on a single host. Kubernetes runs containers across a cluster of hosts and handles scheduling, scaling, networking and self-healing. Most production setups use both: Docker (or BuildKit, or Podman) to produce images, Kubernetes to run them at scale.

Can Docker and Kubernetes be used together?

Yes. You write a Dockerfile, build an OCI image, push it to a registry and reference it in a Kubernetes Deployment. Kubernetes pulls the image and runs it as a pod. The Docker daemon does not run on cluster nodes anymore (containerd does), but Docker is still the most common build tool feeding Kubernetes.

Does Kubernetes still use Docker?

Not as its runtime. Since Kubernetes 1.24 (May 2022), nodes use containerd or CRI-O via the Container Runtime Interface. The dockershim adapter was removed. Docker-built images still run on Kubernetes unchanged because they follow the OCI image specification. Mirantis maintains cri-dockerd for niche cases.

Is Kubernetes replacing Docker?

No. Kubernetes replaced one specific use of Docker (the on-node runtime), but Docker remains dominant for building images, local development and CI. The Docker piece that competed directly with Kubernetes was Swarm and Swarm lost. Build tooling, registries and images are stronger than ever.

Should I learn Docker or Kubernetes first?

Docker first, without question. Get comfortable with containers, Dockerfiles and images on your laptop. Build something. Run it with Docker Compose. Once you understand what a container is and is not, Kubernetes makes sense as orchestration on top of that primitive.

What is the difference between Kubernetes and Docker Swarm?

Swarm is Docker's built-in orchestrator: simpler, tied to the Docker CLI, fine for small clusters. Kubernetes is more powerful, has a far larger ecosystem (Helm, operators, service meshes, GitOps) and is the default in every major cloud's managed offering. For new production workloads, Kubernetes wins.

Can I run Kubernetes locally for learning?

Yes. Minikube spins up a single-node cluster in a VM. Kind runs Kubernetes nodes as Docker containers. k3d runs k3s, a stripped-down distribution from Rancher. Docker Desktop and Podman Desktop both ship one-click Kubernetes. Any of these lets you practise kubectl, Deployments and Helm.

FinOps OS powered by context-aware AI agents.

Start with a 30-day no-cost trial.

Read-only.

No credit card.

No commitment.

Want to assess how your FinOps journey can scale?

Benchmark maturity, close governance gaps, and drive ROI in under 20 minutes

Can your engineering context keep up with the speed of AI?

Start with a 14-day Runtime Accountability Audit. Read-only access. No commitment.

No credit card · No migration · No agents

STAY AHEAD

Can your engineering context keep up with the speed of AI?

Start with a 14-day Runtime Accountability Audit. Read-only access. No commitment.

No credit card · No migration · No agents

STAY AHEAD

Can your engineering context keep up with the speed of AI?

Start with a 14-day Runtime Accountability Audit. Read-only access. No commitment.

No credit card · No migration · No agents

STAY AHEAD