Kubernetes Explained (2026): Pods, Deployments, Services for Beginners

Nov 10, 2026 · 13 min · Ajay Kumar

The first time I used Kubernetes I spent an afternoon deleting a Pod that kept coming back, convinced the cluster was broken. It was working exactly as designed: I had told a Deployment I wanted one copy running, and it kept its promise. Most beginner confusion about Kubernetes comes from that one idea, so this post explains Kubernetes for beginners from the top down: what the control plane and nodes are, what Pods, Deployments, ReplicaSets and Services actually do, where Ingress and Gateway API fit, what ConfigMaps, Secrets and namespaces are for, and, step by step, how a kubectl apply becomes a running container. It is written against Kubernetes v1.37, the current minor release, and ends with a list of things you can ignore for your first few months.

What is Kubernetes, in one paragraph?

Kubernetes is a database of desired state with a set of programs that keep reality matching it. You do not tell it "start three containers". You write down "there should be three copies of this container, reachable on port 80", store that record in the cluster, and a collection of controllers works continuously to make it true, including after a node dies or someone deletes a Pod by hand. Everything else sits on top of that reconciliation loop.

v1.37 arrived on 26 August 2026 and the latest patch, v1.37.2, on 28 October. The upstream project maintains the three most recent minor versions, listed on the releases page. Adoption is no longer the question it was: the CNCF's 2025 annual survey found 82 percent of container users running Kubernetes in production, up from 66 percent in 2023. Whether you need it is a separate question, which I tackled in Is Kubernetes still worth it in 2026?.

Control plane vs nodes: the two halves of a cluster

A cluster has a control plane, which decides, and nodes, which run your containers. The components page lists them, and it is short enough to memorise.

The control plane is:

  • kube-apiserver: the front door. Every read and write, from kubectl, from controllers, from nodes, goes through it. It authenticates the caller, checks authorisation (RBAC), runs admission checks, validates the object and stores it.
  • etcd: a consistent key-value store holding every object. If etcd is lost without a backup, the cluster has forgotten what it was supposed to be running.
  • kube-scheduler: watches for Pods that have no node yet and picks one, based on resource requests, affinity rules, taints and so on.
  • kube-controller-manager: a bundle of controllers (Deployment, ReplicaSet, Job, EndpointSlice, Node and others), each a loop that compares desired state with actual state and acts on the difference.
  • cloud-controller-manager (optional): the part that talks to AWS, GCP or Azure to create load balancers and learn about VMs.

Each node runs:

  • kubelet: the agent that watches the API server for Pods assigned to its node and makes the container runtime start them, then reports status back.
  • a container runtime, such as containerd or CRI-O, which actually pulls images and runs containers.
  • kube-proxy, now marked optional in the docs, which programs node networking rules so that Service addresses work. Some network plugins, Cilium for example, replace it entirely.

On EKS, GKE or AKS you never see the control plane machines, but every error message you read still comes from one of them.

Pods, ReplicaSets and Deployments: what is the difference?

A Pod is the smallest thing Kubernetes schedules: one or more containers that share a network namespace (one IP address, so they talk over localhost) and can share volumes. Pods are disposable. They are not restarted onto a new node when their node dies; they are replaced by new Pods with new names and new IPs. That is why you almost never create a bare Pod.

A ReplicaSet keeps a set number of identical Pods running. If one disappears, it creates another. That is the thing that kept resurrecting the Pod I was deleting.

A Deployment manages ReplicaSets so that you can change the Pod template safely. When you change the image tag, the Deployment creates a new ReplicaSet and scales it up while scaling the old one down. Only changes to the Pod template (spec.template) trigger a rollout; changing the replica count does not. The API defaults in v1.37 are worth knowing because they explain what you see during an update: maxSurge and maxUnavailable both default to 25 percent, revisionHistoryLimit keeps 10 old ReplicaSets around for rollback, and progressDeadlineSeconds reports a rollout as failed after 600 seconds without progress. The Deployment docs cover kubectl rollout undo, which simply scales an old ReplicaSet back up.

In practice you write Deployments, Kubernetes writes ReplicaSets, and you read Pods when debugging. StatefulSets (stable identities) and DaemonSets (one Pod per node) exist, but the Deployment covers most web services.

Each container in the Pod template should declare resource requests, because that is what the scheduler uses to place it. I wrote a separate explainer on requests and limits, and another on the liveness, readiness and startup probes that tell Kubernetes when a container is ready for traffic.

What is a Kubernetes Service, and which type do I need?

Pods come and go and their IPs change, so clients need something stable. A Service gives a set of Pods one virtual IP and a DNS name such as web.default.svc.cluster.local. It finds its Pods by label selector: any Pod whose labels match is a backend. The Service docs describe the mechanism: a controller watches for matching Pods and keeps a list of their addresses in EndpointSlice objects, and kube-proxy or the network plugin turns that list into forwarding rules on every node. A Pod that fails its readiness probe is taken out of the list, which is why readiness probes matter more than most beginners think.

There are four types:

  • ClusterIP (the default): reachable only inside the cluster. Most Services are this.
  • NodePort: also opens the same port on every node, from a default range of 30000 to 32767 (set in the API server source).
  • LoadBalancer: asks the cloud provider for an external load balancer pointing at the nodes. On a laptop cluster this stays pending unless you install something to fulfil it.
  • ExternalName: a DNS alias to something outside the cluster, with no proxying at all.

Ingress or Gateway API: how traffic gets in

For host- and path-based HTTP routing there are two APIs. Ingress is the old one, and the Ingress docs now say plainly that the API "has been frozen": it stays GA and will not be removed, but it gets no new features, and the project recommends Gateway API instead. The most popular controller, ingress-nginx, was retired in March 2026 with no further releases or security fixes, and its GitHub repository is now archived.

Gateway API splits the job into roles: a GatewayClass (which implementation, such as Envoy Gateway, Istio or Cilium), a Gateway (the listener, often owned by a platform team) and HTTPRoutes (owned by app teams). It is installed as CRDs rather than built in; v1.7.0, released on 29 October 2026, promoted HTTPRoute retries to the Standard channel. If you are starting today, learn Gateway API and treat Ingress as something you will meet in older clusters. My ingress-nginx retirement post compares the replacements.

ConfigMaps, Secrets and namespaces

A ConfigMap holds non-secret configuration as key-value pairs that you inject into Pods as environment variables or mounted files, so the same image runs in staging and production. A Secret is the same shape for sensitive values, with one trap every beginner should hear early. The Secrets docs warn that Secrets are "by default, stored unencrypted" in etcd, and the base64 in a Secret manifest is encoding, not encryption. Anyone who can create a Pod in a namespace can read that namespace's Secrets indirectly by mounting them. Secrets are capped at 1 MiB (MaxSecretSize). Turn on encryption at rest, restrict who can read them with RBAC, and do not commit them to Git in plain text.

A namespace is a name scope and a boundary for permissions and quotas. Two teams can each have a Service called api in different namespaces. Without NetworkPolicies, Pods in different namespaces can still talk to each other.

Object What it does Beginner needs it? Docs
Pod One or more containers sharing an IP and volumes Read it, rarely write it Pods
ReplicaSet Keeps N identical Pods running Created for you ReplicaSet
Deployment Rolling updates and rollback over ReplicaSets Yes, day one Deployments
Service Stable virtual IP and DNS name for Pods Yes, day one Service
Ingress HTTP routing; API frozen Only in older clusters Ingress
Gateway, HTTPRoute HTTP routing, role-oriented, CRDs When you expose apps Gateway API
ConfigMap Non-secret config as env vars or files Yes, week one ConfigMaps
Secret Sensitive values, unencrypted in etcd by default Yes, with care Secrets
Namespace Name scope for objects, RBAC and quotas Yes Namespaces

What happens when you run kubectl apply?

Here is the smallest useful pair of objects. It is close to what I use in the step-by-step kind tutorial, where you run it on your laptop.

# web.yaml: a Deployment and a ClusterIP Service (apps/v1 and v1 are the stable APIs)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3                     # desired state: three Pods
  selector:
    matchLabels: { app: web }     # which Pods this Deployment owns
  template:                       # changing anything here triggers a rollout
    metadata:
      labels: { app: web }
    spec:
      containers:
        - name: web
          image: ghcr.io/stefanprodan/podinfo:6.17.0   # pinned tag, never :latest
          ports:
            - containerPort: 9898
          resources:
            requests: { cpu: 50m, memory: 64Mi }      # what the scheduler reserves
          readinessProbe:
            httpGet: { path: /readyz, port: 9898 }    # gate traffic until ready
---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: ClusterIP                 # the default; in-cluster only
  selector: { app: web }          # matches the Pod labels above
  ports:
    - port: 80                    # the Service port clients use
      targetPort: 9898            # the container port traffic goes to

When you run kubectl apply -f web.yaml, this is the sequence:

  1. kubectl reads your kubeconfig, converts the YAML to JSON and sends it to kube-apiserver over HTTPS.
  2. The API server authenticates you, checks RBAC, runs admission controllers (which may add defaults or reject the object), validates it and writes the Deployment and Service to etcd. At this point nothing is running. kubectl apply returns as soon as the records are stored.
  3. The Deployment controller notices a Deployment with no ReplicaSet and creates one. The ReplicaSet controller notices it has zero of three Pods and creates three Pod objects. They have no node yet, so kubectl get pods shows them as Pending.
  4. The scheduler sees unscheduled Pods, filters nodes that can fit 50m of CPU and 64Mi of memory, scores the rest and writes a node name into each Pod.
  5. The kubelet on each chosen node sees a Pod assigned to it, asks the container runtime to pull the image and start the container (ContainerCreating), then reports Running.
  6. Once the readiness probe passes, the EndpointSlice controller adds the Pod's IP to the Service's endpoints, and kube-proxy or the network plugin updates node rules. Only now does traffic to web reach it.

No component calls another directly. Each watches the API server and writes results back, which is why errors show up as statuses and events on objects, read with kubectl describe, rather than as a failed command.

From kubectl apply to a running, ready Pod control plane worker node kubectl apply POSTs desired state as JSON (HTTPS) kube-apiserver authn, RBAC, admission, validate etcd stores the object; nothing runs yet controllers Deployment creates ReplicaSet, Pods kube-scheduler picks a node, sets spec.nodeName kubelet pulls image (CRI), starts containers 1 2 3 4 5 6 Every component except etcd talks only to kube-apiserver: controllers, scheduler and kubelet watch it. When the readiness probe passes, the EndpointSlice controller adds the Pod IP and the Service routes to it. What kubectl get pods shows along the way Pending (steps 3 to 5) ContainerCreating (6) Running, 0/1 ready Running, 1/1 ready
The control loop behind one kubectl apply, based on the Kubernetes components and Service documentation for v1.37. Each arrow is really a watch on kube-apiserver, not a direct call.

What can a beginner safely ignore?

A lot. For your first few months, I would skip:

  • Writing operators and CRDs. You will use CRDs (Gateway API is one), but building controllers can wait.
  • Service meshes. Istio, Linkerd and friends solve problems you do not have with three Services.
  • StatefulSets for databases. Use a managed database while you learn. Running Postgres on Kubernetes is fine later, with an operator.
  • Building your own cluster with kubeadm. Worth doing once to learn, not for running things. Use kind locally and a managed service in the cloud.
  • Helm chart authoring. Install charts, sure. Write plain YAML first so you understand what the chart templates.

Do not skip resource requests, readiness probes, labels, namespaces, and reading kubectl describe and kubectl get events. When something breaks, it is almost always one of three states, and each has its own playbook: Pending, ImagePullBackOff and CrashLoopBackOff.

This is also where ordinary DevOps discipline earns its keep. Applying YAML by hand is how clusters drift into states nobody can reproduce, so keep manifests in Git, pin image tags and review changes. A coding agent can write a Deployment in seconds; it is the review, the version pinning and the rollback plan that keep it from paging you.

FAQ

What is the difference between a Pod and a Deployment?

A Pod is one running instance of your containers, with its own IP, and it is not replaced if it dies on its own. A Deployment is a controller that keeps a desired number of Pods running through a ReplicaSet and handles rolling updates and rollback. You write the Deployment and let it manage Pods.

Is a Kubernetes Service a load balancer?

A ClusterIP Service is an internal virtual IP that spreads connections across ready Pods using node-level rules, so it does basic layer 4 load balancing inside the cluster. For HTTP routing by host or path you need Gateway API or an Ingress controller on top.

Are Kubernetes Secrets encrypted?

Not by default. The docs state that Secrets are stored unencrypted in etcd, and base64 is only an encoding. Enable encryption at rest (managed services usually offer a KMS option), limit access with RBAC, and consider an external secret store.

Should I learn Ingress or Gateway API in 2026?

Gateway API. Ingress is frozen, still supported and not going away, so you will meet it in existing clusters, but new features land in Gateway API, and the best-known Ingress controller, ingress-nginx, stopped receiving fixes in March 2026.

If I were starting from zero this quarter, I would install kind, apply the two objects above, break them on purpose (wrong image tag, wrong selector, failing probe) and read what kubectl describe says each time. One afternoon of that teaches more than a week of diagrams. The kind and kubectl guide walks through exactly that, version-pinned, with a troubleshooting table for the errors you will hit. After that, I would move one real service to a managed cluster with its manifests in Git from the first commit.

Related: Is Kubernetes Still Worth It in 2026? v1.37, 82% in Production, Real Costs, Kubernetes Requests and Limits Explained: 8% CPU Used, Resize GA in 1.35 and Ingress NGINX Is Retired: What Replaces It and How to Choose in 2026.

Spot an error or something out of date? Email me and I will fix it and log it on the corrections page.

I'm Ajay Kumar — I build and operate PandaStack, an open-source Firecracker microVM cloud for AI agents, and write about the infrastructure and tooling around it.

Need this kind of infrastructure work? See what I do or email hello@ajayk.sh.


Related