Containers have become the modern standard for packaging and deploying applications. This episode covers why containers beat VMs, the division of roles in managed Kubernetes, serverless container runtimes, and a comparison of EKS, ECS, Fargate, GKE, Cloud Run, AKS, and Azure Container Apps, complete with kubectl examples and a Deployment manifest.

In episode 11 we handled small pieces of logic with serverless. But many applications are uncomfortable being forced into a function shape — they have full frameworks, many dependencies, long-running processes, and ports that must stay open. For applications like that, the modern deployment method is containers, and the way to manage them at scale is Kubernetes.
Episode 12 covers why containers became the deployment standard, how containers beat VMs, what managed Kubernetes means, who manages what, and a comparison of EKS, ECS, Fargate, GKE, Cloud Run, AKS, and Azure Container Apps. You'll also see first-hand kubectl commands and your first Deployment manifest.
Before containers, the biggest deployment problem was "it works on my machine". Applications that worked on a developer's laptop often broke in production because of different runtime versions, libraries, and configuration. Containers solve this radically: the application and its entire environment are packaged into a single image, then run anywhere with the same result.
| Aspect | VM | Container |
|---|---|---|
| Virtualization unit | Entire OS (hypervisor) | Process + host OS kernel |
| Size | Gigabytes | Megabytes |
| Boot time | Minutes | Seconds |
| Density per host | Tens | Hundreds to thousands |
| Consistency | Depends on OS configuration | Identical everywhere |
Tip
Think of shipping goods. A VM is a truck that carries its entire factory — big, slow, and wasteful. A container is a shipping container: filled, standard-sized, and transportable by any ship, truck, or train in the world. That standardization transformed global logistics — and made containers change the way software is distributed.
Container images are built from a Dockerfile, stored in a registry (like Docker Hub, ECR, GCR, or ACR), then pulled and run by a host. Every code change produces a new image with a version tag — a reproducibility that was impossible to achieve with manual servers.
Running one container is easy. Problems arise when there are dozens of containers: who places containers on which machine, who restarts crashed containers, who routes traffic to the right container, and how to increase the number of containers when load rises. The answer is orchestration, and the de facto standard is Kubernetes (K8s).
Kubernetes splits the world into two levels:
You no longer interact with containers one by one; instead you declare the desired state — "run 3 replicas of this application" — and Kubernetes works to keep reality always matching your intent.
Running Kubernetes yourself means operating the complex control-plane components — the API server, scheduler, controller manager, and etcd. Managing all of that yourself (self-managed) only makes sense at enormous scale. For almost every team, the right choice is managed Kubernetes: EKS (AWS), GKE (GCP), and AKS (Azure).
| Component | Managed by provider | Managed by you |
|---|---|---|
| Control plane (API server, scheduler, etcd) | Yes — HA and automatic patching | No |
| Worker nodes (machines where containers run) | No | Yes — version, size, count, patches |
| Pods and workloads | No | Yes |
| Network policies, storage, security | No | Yes |
Important
This split matters because it determines your responsibilities. The provider guarantees its control plane stays alive, but you remain fully responsible for worker nodes — their Kubernetes version, broken nodes, capacity. Think of it like renting a building: the property manager (provider) guarantees the building's electricity and elevators, but you're the one filling, organizing, and maintaining the contents of each room (worker nodes and workloads).
Once the cluster is available, you interact with it via kubectl. Checking the health of worker nodes:
kubectl get nodes
NAME STATUS ROLES AGE VERSION
node-a1x9 Ready <none> 12d v1.30.2
node-b3z2 Ready <none> 12d v1.30.2
node-c7w4 Ready <none> 10d v1.30.2A Ready status on all nodes means the control plane can schedule containers on those machines. Next, the application is declared in a YAML manifest — a source of truth that can live in git rather than console clicks:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
labels:
app: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web-app
image: company/web-app:1.4.0
ports:
- containerPort: 8080This manifest declares: "always ensure there are 3 replicas of the web-app application from image version 1.4.0". Sending the manifest to the cluster is done with kubectl apply -f deployment.yaml:
kubectl apply -f deployment.yaml
deployment.apps/web-app createdNote
Notice the pattern you'll encounter throughout Kubernetes: you declare the desired state, and Kubernetes executes it. If a pod dies, Kubernetes creates a replacement to keep the replica count at three. If you change the image to a new version, Kubernetes performs a rolling update gradually without downtime. This is called self-healing and declarative — the foundation that will be the main ingredient of the Infrastructure as Code episode.
Managed Kubernetes gives you full control, but still leaves work behind: choosing node sizes, managing scaling, and keeping nodes healthy. For applications that want to use containers without thinking about nodes at all, there's the serverless container category:
| Aspect | Managed Kubernetes | Serverless container |
|---|---|---|
| Node management | You handle worker nodes | Provider, entirely |
| Scaling | Nodes and pods automatic | Down to zero (scale-to-zero) |
| Control | Full (network, storage, flexibility) | Limited to runtime configuration |
| Best for | Complex applications, custom workloads | Request- and event-based applications |
Tip
Steer decisions like this: need full control and diverse workloads, use managed Kubernetes; only need to run a container that serves requests, use a serverless container. Start with the simpler option, move up to Kubernetes only when a real need appears — don't build a cluster for an application Cloud Run or Fargate could handle.
| Need | AWS | GCP | Azure |
|---|---|---|---|
| Managed Kubernetes | EKS | GKE | AKS |
| Managed container orchestrator | ECS | Cloud Run | Container Apps |
| Serverless container | Fargate | Cloud Run | Azure Container Apps |
| Container registry | ECR | Artifact Registry | ACR |
The concepts behind these names are identical: container images, a managed orchestrator or runtime, and a registry for storing images. Moving between clouds means learning new commands, but the way of thinking — images, deployments, replicas, services — stays the same.
In this episode 12 you understood why containers became the deployment standard — the application is packaged with its environment so it runs identically anywhere — and how orchestration handles dozens of containers at once. You also understood the division of roles in managed Kubernetes: the provider manages the control plane, you manage worker nodes and workloads. Finally, you were introduced to serverless containers like Fargate, Cloud Run, and Container Apps that eliminate node management entirely, plus hands-on practice with kubectl get nodes and kubectl apply -f deployment.yaml.
The keys to take away:
Your application can now be deployed the modern way. But once the application is live, users around the world ask: why is the same page slow in Jakarta and fast in London, and how does your domain name always find its way to the right server? Episode 13 answers both questions: Content Delivery Network (CDN) & Cloud DNS — delivering content from the nearest point and translating domain names intelligently.