DEV Community

Amaresh Pelleti
Amaresh Pelleti

Posted on Originally published at devtoolhub.com

Kubernetes in 2026: The Complete Guide for DevOps Teams

Originally published on DevToolHub.

Kubernetes runs the container workloads behind most production systems built since 2018, and running it well in 2026 means owning a lot more than the base API: node pools, ingress controllers, secrets management, cost controls, and a GitOps pipeline all sit on top of the cluster you actually installed. This page pulls together everything the site covers on Kubernetes into one categorized reference, organized by what you're actually trying to do instead of a beginner-to-advanced course. Every section ends with a real recommendation, not a definition you could get from the docs. If you want the deep walkthrough for any topic, follow the linked article. This page tells you what to prioritize, what to skip, and what actually breaks once you're running this in production.

Table of Contents

  • Architecture: What's Actually Running When You Say "Cluster"
  • Workloads: Pods, Deployments, and What Not to Run Bare
  • Networking: Services, Ingress, and Network Policies
  • Storage: Persistent Volumes and Stateful Workloads
  • Security: RBAC, Secrets, and Runtime Protection
  • Observability: Knowing Something's Wrong Before Your Users Do
  • Cost and Scaling: HPA, VPA, and Not Wasting Money
  • CI/CD and GitOps: Getting Code Into the Cluster
  • What Actually Breaks in Production
  • Production Operations: The Verdict

Architecture: What's Actually Running When You Say "Cluster"

A Kubernetes cluster is two separate systems wired together, not one thing. The control plane — kube-apiserver, etcd, kube-scheduler, and kube-controller-manager — makes every decision about what should run and where. The worker nodes — kubelet, kube-proxy, and a container runtime — actually run your containers and report status back. Kubernetes' own architecture docs draw this line explicitly: the API server is the only component that talks to etcd directly, and every other piece of the system, including kubectl and every controller, goes through it.

This matters because etcd is the single source of truth for cluster state, and it's a distributed consensus store — it needs a quorum, more than half its members, to accept writes. Lose quorum and the control plane stops accepting changes, even if every node is still running your workloads fine. A documented kops incident shows exactly this failure mode: a single master reboot triggered communication timeouts across a three-node etcd cluster, and the operator had to reboot all three masters to restore quorum. The same failure recurred three times over six weeks before the underlying network issue was found.

Managed Kubernetes (EKS, GKE, AKS) hides the control plane from you entirely, which is the right default for almost everyone. You don't want to be the one debugging etcd quorum at 2 a.m. If you're managing your own control plane with kops, kubeadm, or similar, budget real operational time for etcd: backups, disk I/O monitoring, and an odd number of members, 3 or 5, never even. For day-to-day cluster interaction, kubectx and K9s are the two tools worth learning before you touch raw kubectl for anything beyond one cluster. If you're still mapping the basic object model, the site's Kubernetes 101 primer covers pods, deployments, and services from zero.

[IMAGE: articles/images/2026-08-04-kubernetes-complete-guide-diagram.png | alt: "kubernetes guide architecture diagram showing control plane and worker node components"]

Verdict: unless you have a dedicated platform team whose job is etcd operations, use managed control planes. Self-managing the control plane buys you configuration flexibility you almost certainly don't need, in exchange for a failure mode — quorum loss — that takes down everything at once.

Workloads: Pods, Deployments, and What Not to Run Bare

A Pod is the smallest unit Kubernetes schedules, but the docs are direct about this: "you'll rarely create individual Pods directly... even singleton Pods," because Pods are ephemeral by design and disappear the moment a node fails. Wrap every workload in a Deployment, StatefulSet, DaemonSet, or Job — the controller is what notices a Pod died and replaces it. A bare Pod that crashes just stays dead until someone notices.

Stateless workloads, like web servers and API backends, belong in Deployments, where any replica can take any request and pods are interchangeable. Stateful workloads (databases, queues, anything with local identity or ordered startup) need StatefulSets, which give each pod a stable name and stable storage across restarts. The site's stateful-vs-stateless breakdown covers the deployment YAML differences directly. Sidecars, a second container in the same pod handling logging, a proxy, or data sync, are the right pattern when two processes need to share a network namespace and lifecycle. The sidecar pattern guide has the three common use cases.

Resource requests and limits are not optional tuning — they're what the scheduler uses to place pods and what decides which pods survive a node running out of memory. Kubernetes enforces CPU and memory differently: CPU limits throttle, the kernel just restricts access, while memory limits kill — the OOM killer terminates the container. A pod with no requests set gets BestEffort QoS and is first in line for eviction the moment a node is under pressure. That's not a hypothetical; it's the documented eviction order. The full requests-and-limits guide has the actual numbers to set.

Verdict: set requests and limits on every container before it goes anywhere near production, full stop. Skipping this doesn't save you time — it just moves the decision about which pods die under pressure from you to the kubelet's eviction manager, and it won't pick the pod you'd have picked.

Networking: Services, Ingress, and Network Policies

Every pod in a Kubernetes cluster can reach every other pod by default — there's no firewall between them until you add one. The NetworkPolicy docs state this plainly: pods are "non-isolated" for both ingress and egress until a NetworkPolicy selects them, and a policy only takes effect if your CNI plugin actually implements NetworkPolicy enforcement. Creating the YAML with a plugin that doesn't support it does nothing, silently. Calico, Cilium, and a handful of others support it; not every CNI does.

Services give you stable networking identity inside the cluster (ClusterIP, NodePort, LoadBalancer), but getting external HTTP traffic in has changed shape recently. Ingress-NGINX, the ingress controller most clusters used for years, is already retired — the project reached end-of-life on March 24, 2026, and the repo is now read-only: no new features, no bug fixes, no CVE patches. The full migration path is covered here. The replacement is the Gateway API, which is more expressive than Ingress ever was, with native traffic splitting and a better multi-team ownership model, but that means rewriting your routing config, not just swapping a controller. Node pool networking — how pods communicate across nodes via overlay or direct routing — is the layer underneath all of this that most people never look at until something breaks.

Segmentation between namespaces, or between a frontend tier and a database, needs an explicit default-deny policy plus allow rules. The network policies guide has the default-deny-all pattern and a debug-pod technique for testing a policy without breaking prod traffic to find out.

Verdict: don't wait for a security review to add network policies. Start every namespace with default-deny and add allow rules as you actually need them — retrofitting network segmentation into a cluster where everything already talks to everything is a much bigger project than building it in from day one. And if you're still on Ingress-NGINX, the Gateway API migration isn't a roadmap item anymore — you're running an unsupported, unpatched ingress controller in production right now, with no CVE fixes coming.

Storage: Persistent Volumes and Stateful Workloads

A PersistentVolume is cluster storage with a lifecycle independent of any pod, and a PersistentVolumeClaim is how a pod requests it. The docs describe a PVC as similar to a Pod in that a Pod consumes node resources while a PVC consumes PV resources. In practice you almost never hand-provision PVs anymore — a StorageClass triggers dynamic provisioning, and the cloud disk gets created on demand when a PVC asks for it.

The access mode matters more than people expect. ReadWriteOnce means exactly one node can mount the volume at a time, which is a common source of confusion when a StatefulSet pod gets rescheduled to a new node and the old pod hasn't released the volume yet, causing a stuck pending pod. ReadWriteMany needs a storage backend that actually supports concurrent multi-node access — NFS-based, mostly, since most cloud block storage doesn't.

Reclaim policy is the other thing worth checking before you need it. Delete removes the underlying disk when the PVC is deleted; Retain leaves it orphaned for manual cleanup. Running a database on Kubernetes without checking which one your StorageClass defaults to is how people accidentally delete production data during a routine PVC cleanup.

For actual stateful production workloads, running a database as a raw StatefulSet is more operational work than most teams want — an operator handles failover, backups, and replication for you. CloudNativePG on Kubernetes is the concrete example: it's a CNCF-listed operator that automates PostgreSQL HA, point-in-time recovery, and connection pooling instead of you scripting failover yourself.

Verdict: if you're running a database in Kubernetes, use an operator — don't hand-roll StatefulSet failover logic. And check your StorageClass's reclaim policy before it matters, not after.

Security: RBAC, Secrets, and Runtime Protection

RBAC in Kubernetes is purely additive. There's no "deny" rule, only grants, which means the default failure mode is over-permissioning, not under. A Role or ClusterRole defines what actions are allowed; a RoleBinding or ClusterRoleBinding attaches that to a user, group, or service account. Handing out cluster-admin because a RoleBinding is annoying to write is the single most common RBAC mistake, and the site's RBAC tutorial is built specifically around fixing that habit, including how to check exactly what a service account can do with kubectl auth can-i.

Secrets need to leave the cluster's default Secret object as soon as you're running anything real — base64 is not encryption, and anyone with get access to Secrets in a namespace can read every credential in it. HashiCorp Vault's injector or an equivalent external secrets operator solves this by injecting secrets at pod startup instead of storing them as cluster objects at all.

Runtime security is the layer most teams skip until an incident forces the conversation. RBAC and network policies stop unauthorized access; they don't tell you when a container is doing something it shouldn't — a shell opening inside a production pod, a package manager launching at runtime. Falco watches syscalls via eBPF and fires on exactly that kind of behavior, independent of which CNI you're running. If you're on AKS specifically, the AKS security guide covers the platform-specific layer (Azure AD integration, Defender for Containers) on top of the cluster-level controls that apply everywhere.

Verdict: RBAC and default-deny network policies are the floor, not the finish line. If your security posture stops at "we have RBAC," you have detection for nothing. Add runtime monitoring before you need it during an incident, not after.

Observability: Knowing Something's Wrong Before Your Users Do

Kubernetes doesn't tell you when an application is actually unhealthy — it tells you when a pod fails a liveness probe, which is a much narrower signal. A pod that's up, passing every probe, and returning 500s to real users looks completely healthy from kubectl get pods. That gap is why cluster metrics (CPU, memory, restart counts) and application metrics (error rate, latency, saturation) need to be two separate dashboards, not one.

The failure mode that catches people is assuming pod restarts equal problems and pod stability equals health. Neither is reliably true. A CrashLoopBackOff is loud and easy to alert on. A slow memory leak that gets OOMKilled every six hours and silently restarts is much harder to notice unless you're actually tracking restart counts and correlating them against deploy timestamps, not just eyeballing pod status.

Distributed tracing matters more in Kubernetes than in a monolith, because a single user request can hop through a dozen pods across multiple nodes before it returns. Without tracing, "why is this slow" turns into checking every service in sequence. For workloads with real GPU or resource contention, like LLM inference running on Kubernetes, the metrics that matter (queue depth, token throughput, GPU utilization) aren't things kubectl top shows you at all. You need workload-specific exporters, not generic cluster metrics.

Verdict: if your alerting is built entirely on pod restart counts and CPU thresholds, you'll catch infrastructure problems and miss application problems — the ones your users actually notice first. Instrument the application layer with the same seriousness as the cluster layer, not as an afterthought once the cluster metrics are already in place.

Cost and Scaling: HPA, VPA, and Not Wasting Money

The Horizontal Pod Autoscaler adjusts replica count based on observed metrics through a control loop that runs, by default, every 15 seconds, using the formula desiredReplicas = ceil(currentReplicas × currentMetric / targetMetric). It only works if your containers have resource requests set — without them, CPU utilization percentage is undefined and HPA has nothing to scale against. That's the same requests-and-limits dependency from the Workloads section showing up again: skip it there, and autoscaling breaks downstream too.

HPA has real limits worth knowing before you rely on it. It doesn't scale DaemonSets. Default stabilization windows are 0 seconds for scale-up — HPA reacts to a spike immediately — and 5 minutes for scale-down, specifically to prevent flapping on the way back down. Shortening that scale-down window without understanding why it's there is how you get a pod count that oscillates every few minutes under bursty traffic. And HPA scaling pods doesn't mean your downstream dependencies scale with it: a spike that takes API replicas from 3 to 15 can just as easily exhaust a fixed-size database connection pool faster than it fixes the original bottleneck.

Cost is mostly a resource-allocation problem wearing an infrastructure costume. The site's cost optimization breakdown puts real numbers on this — a large share of container spend goes to idle, overprovisioned resources rather than actual compute — and spot instances typically run well below on-demand pricing for workloads that can tolerate interruption. Node pool design is where that decision actually gets made: separate pools for stateless, interruptible workloads on spot capacity versus stateful workloads that need guaranteed nodes.

Verdict: don't turn on HPA before requests and limits are set correctly — it'll scale off a number that doesn't mean what you think it means. And don't chase cost savings from spot instances before you've fixed overprovisioning; the overprovisioning waste is usually bigger than the on-demand-versus-spot price gap.

CI/CD and GitOps: Getting Code Into the Cluster

GitOps means your Git repository is the single source of truth for what's running in the cluster, and a controller inside the cluster pulls changes rather than your CI pipeline pushing them. The site's GitOps explainer covers why the pull model is the meaningful difference from a normal CI/CD deploy step, not just a rebrand of the same workflow. The practical benefit is drift detection: if someone runs kubectl edit directly against a live cluster, a GitOps controller notices the live state doesn't match Git and either flags it or reverts it.

ArgoCD is the tool most teams reach for first. The ArgoCD setup guide walks the installation-to-webhook path, and the core behavior is simple to describe even though the setup has real steps: push a change to Git, ArgoCD detects the diff, and it applies the change to match — no manual kubectl apply in the deploy path once it's wired up.

Helm sits underneath most of this as the packaging layer — charts define what gets deployed, GitOps tools decide when. If you're still on Helm 3, the clock is real: Helm 3's final feature release lands September 9, 2026, with security patches stopping February 10, 2027, and Helm 4 renames flags you're probably using in scripts right now, --atomic to --rollback-on-failure and --force to --force-replace. The Helm commands and templating reference is the place to start if you haven't touched Helm charts directly yet.

Verdict: if you're deploying to Kubernetes with a CI pipeline that runs kubectl apply or helm upgrade directly against production, you have no drift detection and no audit trail beyond your CI logs. Move to a pull-based GitOps controller before your next incident review asks how something got into prod and nobody has a clean answer.

What Actually Breaks in Production

Generic Kubernetes advice stops at "test your upgrades" and "monitor your cluster." Here's what specific, documented incidents actually looked like, because the failure modes are more particular than that advice suggests.

Label removal breaking legacy config, silently. Reddit's Pi Day 2023 outage ran over five hours and traced back to a Kubernetes 1.24 upgrade removing the deprecated node-role.kubernetes.io/master label. Kubernetes had renamed "master" to "control-plane" back in the 1.20 series and gave operators years of warning, but a legacy route reflector config still selected nodes by the old label. When 1.24 dropped it entirely, the selector matched nothing, and cluster-wide routing broke. The team's first debugging steps chased a red herring — admission webhook timeouts — before finding the real cause, which is the part worth remembering: the symptom you see first is rarely the actual failure. Kubernetes still ships real deprecations and removals every release; the 1.36 release notes list this cycle's, and the 1.36 breaking-changes breakdown has the current version's full list.

A four-month-old bug, triggered by routine capacity work. Monzo's October 2017 outage took the entire banking platform offline for over an hour. The trigger was mundane — expanding etcd from three to nine nodes and deploying a service with zero replicas — but it hit a real bug in Kubernetes and the etcd client that caused requests to time out after cluster reconfiguration. Kubernetes failed to tell linkerd about valid pod locations, linkerd kept routing to dead IPs, and restarting linkerd instances exposed a version incompatibility that threw NullPointerExceptions parsing empty service responses. Nobody touched anything unusual that day. The bug had been sitting in the stack for four months, waiting for the specific reconfiguration event that would trigger it.

Stale conntrack entries outliving the pod they pointed to. A Freshworks engineering team traced DNS timeouts across an EKS cluster to Linux conntrack retaining stale UDP mappings after a node running two CoreDNS replicas failed. kube-proxy updated the iptables rules correctly and immediately, but client pods kept sending DNS queries through conntrack entries still pointing at the terminated CoreDNS pod IP, and every one of those queries just timed out. They reproduced it deliberately by stacking multiple CoreDNS replicas on one node, then killing that node. The conntrack reconciler that addresses this landed upstream in Kubernetes 1.32's kube-proxy — though reports of related conntrack cleanup edge cases surfaced past that release too, so treat it as a mitigation, not a guarantee. Pod anti-affinity to spread CoreDNS across nodes, so one node failure can't take out DNS resolution cluster-wide, is the fix that doesn't depend on which patch version you're running.

OOMKilled pods that won't stay dead. A Kubernetes issue from 2021, closed as stale without a real fix, documents pods that get OOMKilled, come back up, and then loop into repeated restarts even after memory usage drops back under the limit. The cgroup retains stale memory accounting after the pause container dies, and the pod won't stabilize until someone manually deletes it. It's old, but the exact symptom keeps showing up in current Kubernetes troubleshooting guides — it never got a real upstream fix, the ticket just stopped getting comments. If you see a pod cycling OOMKilled restarts that don't correlate with an actual memory usage pattern, don't just bump the memory limit — check whether you're looking at this exact behavior first.

The pattern across all four: none of these were caused by exotic misconfiguration. They were routine operations — a version upgrade, a capacity change, a node failure, a memory spike — hitting an edge case that only shows up under that specific combination of conditions. That's the actual argument for staging environments that mirror production topology, not a compliance checkbox. Both the Reddit and Monzo incidents involved config or version state that had quietly diverged from what was actually tested.

Production Operations: The Verdict

Running Kubernetes well in 2026 isn't about knowing more YAML fields: it's about which of the sections above you've actually implemented versus which ones you're planning to get to. In order of what actually causes outages when skipped: resource requests and limits first, because everything downstream (HPA, eviction behavior, QoS) depends on them being set correctly. Default-deny network policies second. An actual GitOps pull-based deploy path third. Runtime security monitoring fourth. Observability should run in parallel with all of it, not get bolted on afterward.

If you're managing infrastructure operations at scale, some of this is increasingly automatable rather than manual toil. Claude Managed Agents for DevOps covers running cost analysis, incident triage, and PR infrastructure review as autonomous agent tasks instead of a human running the same kubectl commands every week. That's not a replacement for the fundamentals above — it's what you delegate once the fundamentals are already solid.

The container runtime underneath all of this matters less than it used to for most day-to-day work, but if you're choosing between Docker and Podman for your build pipeline, the comparison is worth reading before you standardize — rootless-by-default matters more in regulated environments than most teams initially assume.

Kubernetes rewards teams that treat the categories above as one connected system instead of a checklist you complete once. A cluster with perfect RBAC and no resource limits will still fall over under memory pressure. A cluster with airtight network policies and no GitOps pipeline will still drift from what's actually documented within a month. Pick the section above where you have the biggest gap right now, follow that link, and fix it before you add anything new.

Frequently Asked Questions

Q: What Kubernetes version should I be running in 2026?
A: Kubernetes maintains support for the three most recent minor versions. 1.37 is due August 26, 2026, at which point the supported window shifts to 1.37/1.36/1.35 and 1.34 moves into its final stretch before end of life in October 2026. Check kubernetes.io/releases for the current three rather than trusting a specific version number in an article, and read the deprecations list before every upgrade regardless — removals like the master node label in 1.24 have caused real production outages when legacy config depended on them.

Q: Do I need a service mesh, or is Kubernetes networking enough on its own?
A: Plain Kubernetes networking (Services, Ingress or Gateway API, NetworkPolicy) covers routing and basic segmentation. You need a service mesh specifically when you need mutual TLS between every pod, fine-grained traffic splitting for canary releases, or consistent retries and circuit breaking across services written in different languages. If none of those apply yet, a mesh is added operational complexity without a matching benefit.

Q: Should small teams run their own Kubernetes control plane?
A: No, in almost every case. Managed control planes — EKS, GKE, AKS — remove etcd operations, quorum management, and control plane upgrades from your team's workload entirely. Self-managing the control plane is a real specialization; budget for it only if you have a dedicated platform team whose job includes exactly that.

Q: What's the single most common Kubernetes production mistake?
A: Deploying containers without resource requests and limits set. It cascades into everything downstream: the scheduler can't place pods intelligently, HPA has no reliable metric to scale against, and the kubelet's eviction manager ends up choosing which pods to kill under memory pressure instead of you choosing.

Quick Summary:

  • Kubernetes 1.37 lands August 26, 2026, shifting the supported window to 1.37/1.36/1.35 as 1.34 heads toward its October 2026 end of life — check kubernetes.io/releases for the current three rather than trusting a specific version number, and read the deprecations list before every upgrade
  • Resource requests and limits are the single dependency underneath HPA, QoS-based eviction, and cost control — set them before anything else
  • Every pod can reach every other pod by default; default-deny NetworkPolicy is the fix, not an optional hardening step
  • Reddit, Monzo, and a Freshworks-run EKS cluster all went down from ordinary maintenance work, not a misconfigured cluster — an upgrade, a capacity change, a node failure, each one colliding with an edge case that's now on record
  • GitOps with a pull-based controller like ArgoCD gives you drift detection that a CI pipeline running kubectl apply directly never will

Start with whichever section above matches your biggest current gap. For most teams still running bare kubectl apply deploys or unset resource limits, that's not the security section — it's the workloads section.

Top comments (0)