What Production Actually Looks Like
After all this, what do cloud providers and production Kubernetes clusters actually run?
The Standard Production Stack
| Cloud | Service | High-Level Runtime | Low-Level Runtime |
|---|---|---|---|
| AWS | EKS | containerd | runc |
| GKE | containerd | runc | |
| Azure | AKS | containerd | runc |
| Oracle | OKE | containerd | runc |
| Red Hat | OpenShift | CRI-O | crun |
| DigitalOcean | DOKS | containerd | runc |
| Linode | LKE | containerd | runc |
Pattern: Everyone uses containerd + runc except Red Hat (CRI-O + crun).
Why containerd Won
2016-2017: Everyone used Docker
├── dockerd
├── containerd (part of Docker)
└── runc
2017: Docker donates containerd to CNCF
└── Now independent, not just a Docker component
2018-2020: Kubernetes removes dockershim
└── Direct containerd integration preferred
2020+: Industry standard
├── AWS adopts containerd
├── Google uses containerd
├── Azure switches to containerd
└── containerd becomes the default
Why containerd Succeeded
- Cloud Native: CNCF project, vendor-neutral
- Kubernetes-native: Designed for CRI from the start (after initial extraction)
- Battle-tested: Proven at scale (Docker’s core)
- Lightweight: No extra daemon layers
- Extensible: Plugin architecture
- Industry consensus: Everyone agreed on one standard
What About Docker in Production?
Docker Swarm
Docker’s native orchestration. Still exists, but:
- Used by < 5% of production workloads
- Kubernetes won the orchestration war
- Limited cloud provider support
- Mostly legacy deployments
Docker as Build Tool
Still dominant in CI/CD:
# GitLab CI, GitHub Actions, Jenkins, etc.
- docker build -t myapp:$CI_COMMIT_SHA .
- docker push registry.example.com/myapp:$CI_COMMIT_SHA
# Deploy to Kubernetes (which uses containerd)
- kubectl set image deployment/myapp app=registry.example.com/myapp:$CI_COMMIT_SHA
Docker for building, containerd for running. Best of both worlds.
Production Runtime Requirements
What enterprises need from their container runtime:
Must Have
- CRI compliance (Kubernetes interface)
- OCI compliance (image/runtime specs)
- Security isolation (namespaces, cgroups)
- Resource management (CPU, memory limits)
- Image management (pull, cache, garbage collection)
- Logging and monitoring
- Stability and reliability
Nice to Have
- Performance optimizations
- Support for multiple OCI runtimes
- Advanced storage drivers
- Observability features
containerd and CRI-O both check all the boxes.
Specialty Runtimes in Production
Beyond containerd/CRI-O, some workloads use specialized runtimes:
gVisor (Google)
What: User-space kernel for extra isolation When: Untrusted workloads, multi-tenant platforms Who: Google Cloud Run, some GKE Sandbox workloads
Normal: App → runc → Linux Kernel → Hardware
gVisor: App → runsc → gVisor kernel → Linux Kernel → Hardware
↑
Extra isolation layer
Kata Containers
What: Each container gets a lightweight VM When: Need VM-level isolation with container UX Who: Azure Confidential Containers, IBM Cloud
Normal: Container = process on host kernel
Kata: Container = process inside micro-VM
↑
Stronger isolation, slight overhead
Firecracker (AWS)
What: Lightweight micro-VMs When: AWS Lambda, AWS Fargate Who: AWS serverless services
The Production Stack Breakdown
What Kubernetes Manages
┌─────────────────────────────────────────┐
│ KUBERNETES │
│ ┌───────────────────────────────────┐ │
│ │ Control Plane │ │
│ │ • API Server │ │
│ │ • Scheduler │ │
│ │ • Controller Manager │ │
│ │ • etcd │ │
│ └───────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────┐ │
│ │ Node (Worker) │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ kubelet │ │ │
│ │ │ └── containerd/CRI-O │ │ │
│ │ │ └── runc/crun │ │ │
│ │ │ └── containers│ │ │
│ │ └─────────────────────────────┘ │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
What You Manage
┌─────────────────────────────────────────┐
│ YOUR RESPONSIBILITY │
│ │
│ • Application code │
│ • Dockerfile / image definition │
│ • Kubernetes manifests (YAML)/Helm │
│ • CI/CD pipeline │
│ • Image registry │
│ │
└─────────────────────────────────────────┘
The runtime is abstracted away. You don’t interact with containerd or runc directly.
Key Takeaways
- containerd + runc is the standard - All major clouds use it
- OpenShift uses CRI-O + crun - Red Hat’s optimized stack
- Runtime is abstracted - Kubernetes hides the implementation
- OCI compliance is key - Ensures portability
- Your workflow stays the same - Build locally, run anywhere
- Docker for building, containerd for running - Common pattern
- Specialty runtimes exist - gVisor, Kata for special needs
- You rarely touch the runtime - It just works
The Final Mental Model
┌────────────────────────────────────────────────────────────┐
│ │
│ YOU: │
│ • Write app │
│ • Define Dockerfile │
│ • Build with Docker/Podman │
│ • Push to registry │
│ • Deploy to Kubernetes │
│ │
│ ───────────────────────────────────────────────────── │
│ │
│ PRODUCTION (abstracted): │
│ • containerd/CRI-O pulls image │
│ • runc/crun creates containers │
│ • Kubernetes manages lifecycle │
│ │
│ ───────────────────────────────────────────────────────── │
│ │
│ RESULT: │
│ • Your app runs reliably │
│ • Platform handles the complexity │
│ • You focus on business logic │
│ │
└────────────────────────────────────────────────────────────┘
Summary: What You Should Remember
The Big Picture
Containers are a Linux kernel feature, not a Docker invention. Docker packaged and popularized them.
Three kernel primitives make containers work: namespaces (isolation), cgroups (resource limits), and filesystem virtualization.
Docker has a layered architecture: CLI → dockerd → containerd → containerd-shim → runc → container.
“Runtime” is overloaded terminology:
- Low-level (OCI) runtime = runc, crun, youki — spawns processes
- High-level runtime = containerd, CRI-O — manages lifecycle
OCI standards ensure portability. Images built with Docker work with Podman, containerd, CRI-O.
Kubernetes didn’t drop Docker — it dropped dockershim (a translation layer) and now talks directly to containerd.
containerd vs CRI-O: containerd is general-purpose; CRI-O is Kubernetes-only. Both call the same OCI runtimes underneath.
Podman is daemonless and rootless. CLI-compatible with Docker. Uses crun by default.
Mac and Windows run a hidden Linux VM because containers need Linux kernel features.
Production uses containerd + runc on all major clouds. Your locally-built images work identically.
The Practical Takeaways
Use Docker Desktop for learning and personal projects — it has the best documentation and community support.
Consider Podman if you’re at a larger company (licensing), have limited RAM, or deploy to OpenShift.
Don’t stress about the runtime your cloud provider uses — OCI standards guarantee your images work everywhere.
Understanding the layers helps debug issues: “Is this a dockerd problem? containerd? runc? The kernel?”
The Final Mental Model
"Container" = Linux process with isolation (namespaces + cgroups)
"Docker" = Company + tooling that made containers accessible
"OCI" = Standards that ensure interoperability
"Runtime" = Either the process spawner (runc) or lifecycle manager (containerd)
You build images. Kubernetes runs them. The stack in between is abstracted away.
This guide covered the internals that most developers never need to think about. But when something breaks, or when you’re designing systems at scale, understanding what’s actually happening under docker run makes all the difference.
Comments