How Kubernetes Deployments Work: Inside the Kubernetes Pod Lifecycle (Step-by-Step Guide 2025)

How Kubernetes Deployments Work: Inside the Kubernetes Pod Lifecycle

When you run kubectl create deployment, dozens of Kubernetes components spring into action behind the scenes to bring your application to life. But what actually happens in those few seconds between hitting Enter and seeing your pods running? This guide breaks down the complete journey step by step, revealing how Kubernetes components work together to continuously maintain the desired state of your workloads.

Whether you’re debugging a stuck deployment, optimizing your cluster, or simply curious about what happens under the hood, understanding this process transforms Kubernetes from a “black box” into a predictable, elegant system.

What Happens When You Run kubectl create deployment

Let’s slow down time and watch what happens when you create a deployment. This is where all those components come alive in a choreographed dance.

kubectl create deployment nginx --image=nginx --replicas=3

The moment you hit Enter, here’s the chain reaction that follows across your entire cluster.

Step 1: kubectl Translates Your Command

Before anything touches the cluster, kubectl does some groundwork. It converts your imperative command into a Deployment object—basically a YAML manifest in memory. Then it figures out which API endpoint to hit and sends an HTTP POST request to the API Server.

Behind the scenes: kubectl reads your ~/.kube/config file to find the API Server’s address and your authentication credentials. Every command you run is actually an authenticated REST API call.

This is why kubectl can work from anywhere—your laptop, a CI/CD pipeline, or a bastion host. As long as it has that config file and network access, it can talk to your cluster.

Step 2: API Server Authentication & Validation

The API Server doesn’t trust anyone. When your request arrives, it goes through three security gates:

Authentication: “Who are you?” It checks your credentials—could be a certificate, token, or service account.

Authorization: “Are you allowed to create Deployments in this namespace?” This is where RBAC (Role-Based Access Control) kicks in.

Admission Control: “Does this Deployment meet our cluster policies?” Admission controllers can modify or reject your request. Maybe you forgot to add resource limits, and the LimitRanger admission controller adds defaults for you.

Only after passing all three gates does your Deployment object get written to etcd.

# Watch this process in the API server logs
kubectl logs -n kube-system kube-apiserver-<node> | grep nginx

This three-gate security model is why Kubernetes clusters can safely handle multiple teams and applications. Every request is validated before it ever touches the cluster’s state.

Step 3: etcd Writes the Desired State

Your Deployment is now stored in etcd with a status of “not yet reconciled.” Think of etcd as the cluster’s journal—it just records what should exist, not what actually exists yet.

The Deployment object looks something like this in etcd:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  generation: 1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
status: {}  # Empty for now—nothing exists yet

Notice that status field? It’s empty. This is the gap between desired state (spec) and actual state (status) that controllers will now work to close. This is the fundamental principle of Kubernetes: declare what you want, and the system continuously works to make it reality.

Step 4: Deployment Controller Wakes Up

The Controller Manager runs multiple controllers in a loop—each one constantly watches the API Server for changes. The Deployment Controller is watching for new or modified Deployments.

How it notices: The API Server supports “watch” operations. Controllers don’t poll repeatedly; they establish a long-lived connection where the API Server pushes updates. It’s like subscribing to a live feed rather than refreshing a webpage every second.

When the Deployment Controller sees your new Deployment, it thinks: “I need to create a ReplicaSet to manage these pods.”

It doesn’t create pods directly. That’s not the Deployment Controller’s job. Instead, it creates a child ReplicaSet object and writes it to the API Server.

# You can see this ReplicaSet
kubectl get replicasets
# NAME                  DESIRED   CURRENT   READY
# nginx-5d4f4dd8cd     3         0         0

That cryptic suffix (5d4f4dd8cd) is a hash of the pod template. If you update your Deployment later with a new image, you’ll get a new ReplicaSet with a different hash. That’s how Kubernetes implements rolling updates—by managing multiple ReplicaSets over time.

Step 5: ReplicaSet Controller Takes Over

Now the ReplicaSet Controller notices the new ReplicaSet in etcd. It reads: “I need 3 replicas, but 0 exist.”

Time to create pods.

The ReplicaSet Controller creates three Pod objects—not running containers yet, just the definitions. Each pod gets:

  • A unique name (like nginx-5d4f4dd8cd-x7p2q)
  • Labels matching the selector
  • The container spec (image, ports, resources)
  • An owner reference pointing back to the ReplicaSet

These three Pod objects are written to etcd via the API Server. Their status is “Pending” because nobody’s actually started them yet.

# Watch pods appear in Pending state
kubectl get pods -w
# NAME                     READY   STATUS    RESTARTS   AGE
# nginx-5d4f4dd8cd-x7p2q   0/1     Pending   0          1s
# nginx-5d4f4dd8cd-m9kl3   0/1     Pending   0          1s
# nginx-5d4f4dd8cd-q4rt8   0/1     Pending   0          1s

How the Scheduler Assigns Pods to Nodes

The Scheduler is watching for pods with no assigned node. It sees three Pending pods and gets to work immediately.

For each pod, the Scheduler runs two phases:

Filtering Phase

Which nodes can run this pod? The Scheduler checks:

  • Does the node have enough CPU and memory?
  • Does the pod need a GPU? Does this node have one?
  • Are there taints on the node that the pod doesn’t tolerate?
  • Does the pod have nodeSelector requirements?

Let’s say all three worker nodes pass filtering.

Scoring Phase

The Scheduler ranks the nodes. It considers:

  • Which node is least loaded right now?
  • Should pods spread across zones for availability?
  • Are there preferred node affinities?

After scoring, the Scheduler picks the best node for each pod. Maybe it decides:

  • Pod 1 → worker-node-1 (least loaded)
  • Pod 2 → worker-node-2 (spread across nodes)
  • Pod 3 → worker-node-3 (spread across nodes)

The Scheduler doesn’t start the pods. It just updates each Pod object in etcd with a nodeName field.

# See scheduling decisions
kubectl get pods -o wide
# NAME                     READY   STATUS    NODE
# nginx-5d4f4dd8cd-x7p2q   0/1     Pending   worker-node-1
# ...

Key insight: If no nodes pass filtering, your pod stays stuck in Pending forever. You’ll see events like “0/3 nodes are available: Insufficient memory.” This is one of the most common Kubernetes troubleshooting scenarios.

Kubelet: Bringing Pods to Life

Each Kubelet is watching the API Server for pods assigned to its node. When the Kubelet on worker-node-1 sees a pod with nodeName: worker-node-1, it takes ownership.

Here’s what the Kubelet does, in order:

a) Create the Pod Sandbox

The Kubelet tells the container runtime (containerd, CRI-O, etc.) to create a “pod sandbox.” This is the shared namespace where all containers in the pod will live—shared network, shared IPC, shared volumes.

This is what makes pods different from standalone containers. All containers in a pod share the same network namespace, so they can talk to each other on localhost.

b) Pull the Container Image

If nginx:latest isn’t cached locally, the Kubelet tells the container runtime to pull it from Docker Hub (or whatever registry you specified).

# Check image pull progress
kubectl describe pod nginx-5d4f4dd8cd-x7p2q
# Events:
#   Pulling image "nginx"

This can take time depending on image size and network speed. If the pull fails (wrong image name, authentication issues, registry down), the pod will stay in ImagePullBackOff status.

c) Create and Start the Container

Once the image is downloaded, the Kubelet creates the container with your specified configuration (ports, environment variables, volumes, resource limits).

d) Run Health Checks

If you defined liveness or readiness probes, the Kubelet starts executing them. This determines whether the container is actually healthy and ready to receive traffic.

e) Report Status Back to API Server

The Kubelet updates the Pod object’s status in etcd via the API Server. The pod transitions from “Pending” → “ContainerCreating” → “Running.”

kubectl get pods
# NAME                     READY   STATUS    RESTARTS   AGE
# nginx-5d4f4dd8cd-x7p2q   1/1     Running   0          45s

This happens simultaneously on all three worker nodes. Within seconds, all three pods are running across your cluster.

Step 8: Kube Proxy Updates Network Rules

Now you’ve got three pods running on three nodes. But how do you reach them?

If you created a Service (let’s say with kubectl expose deployment nginx --port=80), the Service Controller assigns it a ClusterIP—a virtual IP address.

Kube Proxy’s job: Make that virtual IP actually work.

Every Kube Proxy on every node watches the API Server for Service and Endpoint changes. When it sees your new Service with three backend pods, it programs iptables rules (or IPVS rules) on its node.

These rules say: “If traffic arrives for ClusterIP 10.96.45.67:80, load-balance it across these three pod IPs.”

# See the endpoints Kube Proxy is load-balancing
kubectl get endpoints nginx
# NAME    ENDPOINTS                                      AGE
# nginx   10.244.1.5:80,10.244.2.7:80,10.244.3.9:80     2m

Why it’s clever: Kube Proxy runs on every node. So when a pod on worker-node-1 makes a request to your nginx Service, the local Kube Proxy handles it instantly—no central bottleneck, no single point of failure.

Kubernetes Self-Healing in Action

Your deployment is running. But the controllers don’t clock out and go home. They keep watching, constantly reconciling desired state with actual state.

Every few seconds:

  • The ReplicaSet Controller checks: “Are there still 3 running pods? If one died, I need to create a replacement.”
  • The Kubelet checks: “Are my containers still healthy? Do I need to restart one?”
  • The Node Controller checks: “Are all nodes still responding? If worker-node-2 went offline, I need to reschedule its pods.”

Let’s say one pod crashes:

  1. Kubelet detects the crash and tries to restart it (if restartPolicy allows)
  2. If restarts fail repeatedly, the pod enters CrashLoopBackOff
  3. ReplicaSet Controller notices the pod isn’t “Ready” anymore
  4. If the pod is truly dead, ReplicaSet Controller creates a new pod
  5. Scheduler assigns it to a node
  6. Kubelet starts it up
  7. Kube Proxy updates its rules to include the new pod IP

This is self-healing in action. You don’t intervene. The cluster converges back to your desired state automatically.

# Simulate a pod dying
kubectl delete pod nginx-5d4f4dd8cd-x7p2q

# Watch it get recreated instantly
kubectl get pods -w

Try it yourself. Delete a pod and watch how fast Kubernetes responds. Usually, you’ll see a replacement pod in “ContainerCreating” status within 1-2 seconds.

The Power of Declarative Systems

Notice what you didn’t have to do?

You didn’t tell Kubernetes “start this container on that node.” You declared what you wanted: “I want 3 nginx replicas running.”

Kubernetes figured out the how. And it keeps figuring it out continuously, correcting drift, adapting to failures, maintaining your desired state.

That’s the power of this architecture. Every component has one job. They communicate through the API Server. They watch etcd. They reconcile constantly.

The components involved in this journey:

  • kubectl – Translates your commands to API calls
  • API Server – Validates and persists every request
  • etcd – Stores the desired state
  • Deployment Controller – Creates ReplicaSets
  • ReplicaSet Controller – Creates Pods
  • Scheduler – Assigns pods to nodes
  • Kubelet – Runs containers on nodes
  • Kube Proxy – Routes traffic to pods
  • Container Runtime – Actually runs the containers

Each component is simple. They just watch for changes and react. But together, they create a resilient, self-healing system that manages thousands of containers across hundreds of machines.

It’s not magic—it’s a well-designed distributed system where simple components create emergent complexity.

Key Takeaways

Understanding the pod lifecycle helps you:

  • Debug faster – When a pod is stuck in Pending, you know to check scheduler events
  • Optimize performance – You understand where bottlenecks can occur (image pulls, scheduling, etc.)
  • Design better – You can write manifests that work with the system, not against it
  • Troubleshoot confidently – You know which component to investigate for different issues

The next time you run a kubectl command, you’ll see the entire machine in motion—controllers watching, schedulers deciding, kubelets executing, all working together to maintain your desired state.

Frequently Asked Questions

Why does my pod stay in Pending status?

The Scheduler couldn’t find a suitable node. Check kubectl describe pod for events like “Insufficient CPU” or “No nodes available.” This usually means resource constraints or node taints blocking scheduling.

What’s the difference between a Deployment and a ReplicaSet?

Deployments manage ReplicaSets and provide rolling updates. ReplicaSets just ensure a specific number of pod replicas are running. You should always use Deployments—they give you ReplicaSets plus update strategies.

How fast is Kubernetes self-healing?

Typically 1-3 seconds from pod failure to replacement pod creation. The actual startup time depends on image pull and container initialization. For already-pulled images, you can see new pods running in under 10 seconds.

Can I see these components in action?

Yes! Use kubectl get events --watch to see the event stream. Use kubectl logs -n kube-system <controller-pod> to see controller logs. Enable audit logging on your API Server to see every API call.

What happens if the Scheduler crashes?

New pods won’t get scheduled, but existing pods keep running. Kubernetes control plane components are themselves managed as pods (in most setups), so they self-heal too. The Scheduler will restart and catch up on pending pods.

Next Steps: Now that you understand the pod lifecycle, dive deeper into Kubernetes networking to learn how pod-to-pod communication works across nodes, or explore advanced scheduling with node affinity and pod topology spread constraints.

Want to test your understanding? Try deploying an application with specific resource requests and node selectors, then watch the entire process with kubectl get events --watch and see each component in action.

Similar Posts

Leave a Reply