I am a Pod. You have probably seen my name in a terminal — something like checkout-api-7f4b9c8d6-xk2lm — and wondered why it looks like a password someone lost on a keyboard.
Fair question.
Before I existed, there was only a stable application: code that already ran reliably as a container image in a registry. Someone packaged it, tagged it, and pushed it. That image is not me. It is my blueprint. I am what happens when Kubernetes decides to instantiate that blueprint on a specific Node at a specific moment.
This post is my side of the story: how I come into being, who my contacts are, how traffic reaches me, and why I disappear without drama when the cluster moves on.
Before I exist: the app arrives as desired state
Nothing in Kubernetes starts with “run this container on that machine”. It starts with a description.
A platform engineer, a developer, or a GitOps pipeline submits a Deployment — usually YAML, sometimes Helm, sometimes a click in OpenShift that ends up as YAML anyway. The Deployment says things like:
- Run three copies of this app.
- Use image
registry.example.com/checkout-api:2.4.1. - Each copy listens on port 8080.
- Mount configuration from this ConfigMap.
- Inject credentials from this Secret.
- Label me so the Service can find me.
That manifest travels to the API server, the front door of the cluster. The API server validates it, stores it in etcd, and returns success. At this point, nobody has started my container yet. The cluster has only recorded a desired state.
I find that distinction comforting. Birth is not instant. Birth is a chain of decisions.
My managers: Deployment and ReplicaSet
I do not report directly to humans. I report to controllers.
The Deployment controller reads the Deployment and creates or updates a ReplicaSet. The ReplicaSet’s job is blunt and useful: keep exactly N Pods running that match a template. If one of us dies, create another. If the template changes, manage the transition.
The ReplicaSet controller then creates my Pod object — still just data in the API, not yet a running process. My name will include a random suffix. That is intentional. I am not a pet with a permanent name tag. I am cattle with a shift roster.
If you are debugging “why is there no Pod?”, look up the chain:
kubectl get deployment,rs,pods -n <namespace> -l app=checkout-api
kubectl describe deployment checkout-api -n <namespace>
If the Deployment is happy but I never appear, the ReplicaSet or admission policies are often where the story stops.
Finding a home: the Scheduler and the Node
My Pod object exists, but I have no Node yet. I am Pending.
The Scheduler watches unassigned Pods. It reads my resource requests, tolerations, affinity rules, and the state of every Node. It picks a Node that can host me — enough CPU and memory, matching labels, no blocking taints, volume constraints satisfied.
When it decides, it writes my Node assignment back to the API. Now the kubelet on that Node is responsible for me.
Think of the Scheduler as air traffic assigning a gate. The kubelet is the ground crew that actually connects the jet bridge.
Waking up: image pull, mounts, and the first breath
The kubelet on my Node sees that I should exist. Its job is to make my actual state match my spec.
Typical sequence:
- Pull the container image from the registry (unless it is already cached on the Node).
- Create my network namespace and attach me to the cluster network (CNI).
- Mount ConfigMaps and Secrets as files or environment variables.
- Attach PersistentVolumeClaims if I need durable storage.
- Start my container with the configured command, args, and security context.
My ConfigMap is the briefing folder: feature flags, log levels, public URLs. My Secret is the locked drawer: database passwords, API tokens, TLS keys. If a Secret is missing or a volume mount path is wrong, I may never reach Running. The kubelet will retry; Events on my Pod object will tell the human story.
kubectl describe pod <pod-name> -n <namespace>
kubectl get events -n <namespace> --field-selector involvedObject.name=<pod-name>
When my main container process starts, I am alive in the Linux sense. Kubernetes may still consider me NotReady. That gap matters.
My address book: labels, Service, and DNS
Inside the cluster, other workloads do not dial my Pod IP by heart. Pod IPs change when I am replaced. They call a Service instead.
The Service selects Pods by labels — key/value pairs the ReplicaSet template stamped on me at creation, like app=checkout-api. The Service gets a stable ClusterIP and a DNS name:
checkout-api.<namespace>.svc.cluster.local
When another Pod resolves that name, CoreDNS answers with the Service IP. kube-proxy (or a CNI implementation with equivalent behaviour) forwards traffic to one of us — usually one Pod that passes readiness checks.
So my social circle for inbound traffic looks like this:
| Contact | Role for me |
|---|---|
| Service | Stable phone number; load-balances to ready Pods like me |
| CoreDNS | Directory; turns names into Service addresses |
| Other Pods | Clients, backends, sidecars in the same Pod |
| Ingress / Route | Optional front door from outside the cluster |
Outbound, I use the same DNS system. A short name like postgres works because the kubelet configured search domains in my /etc/resolv.conf. That convenience also hides namespace mistakes — worth remembering when a connection “works in staging but not production”.
The health checks: when I am allowed to work
Not every running Pod receives traffic immediately.
The readiness probe asks: “Is this instance ready for its job?” Maybe my JVM is still warming up. Maybe I need to run migrations on first boot. Until readiness succeeds, the Service skips me. I can be Running and still invisible to customers — which is better than sending traffic to a half-started process.
The liveness probe asks a harder question: “Is this instance beyond help?” If I deadlock or my process hangs, the kubelet kills my container and restarts it according to my restartPolicy. Readiness protects others from me. Liveness protects the cluster from a stuck me.
Humans inspect what the kubelet sees:
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.status.conditions}'
kubectl logs <pod-name> -n <namespace> --previous
A working day: logs, metrics, and exec
Once I am Ready, requests arrive on my container port. The application inside me handles HTTP, gRPC, queue messages — whatever it was built for. Kubernetes does not understand my business logic. It understands containers, probes, and resource limits.
Logs go to stdout/stderr. The kubelet collects them; your platform forwards them to Loki, Elasticsearch, or CloudWatch. When humans run kubectl logs, they are reading my diary, not logging into my filesystem like SSH on a VM.
Metrics may come from a /metrics endpoint scraped by Prometheus, or from a sidecar container in the same Pod that exports what the app exposes locally. We share a network namespace — localhost between containers is real.
kubectl exec is a human dropping into my container for debugging. Useful in incidents; not a daily operations pattern. If fixing production requires exec every week, the gap is probably in config, probes, or the Deployment template.
My resource requests and limits are the fuel plan the Scheduler used to place me and the ceiling the kubelet enforces. Out-of-memory kills are abrupt. CPU throttling is slower and sneakier. I feel both, even if the app inside me reports them as mysterious latency.
Who else knows I exist?
Beyond my direct managers, a few cluster components watch or influence me:
- API server — source of truth; every controller reads and writes here.
- etcd — where desired and observed state is stored durably.
- Admission controllers / webhooks — may mutate or reject my spec before I am even created (policies, defaults, sidecar injection).
- ServiceAccount — my identity if I call the Kubernetes API from inside the cluster.
- NetworkPolicy — firewall rules between Pods; can silently block traffic I expect to work.
- Horizontal Pod Autoscaler — tells the Deployment to change replica count when metrics say so; more siblings, not a bigger me.
I am one row in a large spreadsheet the cluster reconciles continuously.
Updates: when a new version of me arrives
The application image was stable — until it was not. Someone ships checkout-api:2.5.0.
They change the Deployment template, not me personally. The Deployment controller creates a new ReplicaSet with the updated template and gradually scales it up while scaling the old ReplicaSet down. That is a rolling update.
New Pods appear with the new image. Old Pods like me receive SIGTERM. If my app handles that signal gracefully — finish in-flight requests, close connections — customers notice little. If not, the terminationGracePeriodSeconds countdown runs out and the kubelet sends SIGKILL.
Optional preStop hooks give me a few seconds to drain before the Service removes me from rotation. That small hook prevents a surprising number of 502 errors.
When the rollout finishes, I am gone and my replacements answer to the same Service name. From the client’s perspective, the phone number did not change — only the crew behind it.
kubectl rollout status deployment/checkout-api -n <namespace>
kubectl rollout history deployment/checkout-api -n <namespace>
How my life ends
Pods die for many ordinary reasons:
- Rolling update replaced me.
- Node drained for maintenance.
- Replica count scaled down.
- Liveness probe failed repeatedly.
- Node failed; my replacement was scheduled elsewhere.
- Someone deleted the Deployment; all of us go.
Deletion is a process, not a switch flip. I enter Terminating. Endpoints controllers remove me from Service backends. The kubelet sends SIGTERM, waits, then SIGKILL if needed. Volumes detach. My IP is recycled. My name will not return — a new Pod with a new suffix takes my place.
That is not failure. That is the design.
What I want you to remember
If you are learning Kubernetes, it helps to invert the usual viewpoint. Humans start from kubectl and YAML. I start from a Node the Scheduler chose and a template the Deployment wrote.
The practical habits that fall out of my story:
- Change the Deployment, not the Pod — unless you are doing a deliberate one-off experiment.
- Trust the Service name, not my IP — I will not keep either for long.
- Read Events and describe output — that is how I tell you why I am Pending, CrashLoopBackOff, or NotReady.
- Treat probes and graceful shutdown as part of the app — they decide whether updates feel smooth or chaotic.
- Expect replacement — debugging “this exact Pod” matters in the moment; operating the workload means thinking in replicas and desired state.
I am a Pod. I run one chapter of a stable application for a while, do my job behind a stable Service name, and make room for the next copy when the cluster moves on.
That is a full life in Kubernetes terms — and honestly, a pretty good one.