Back to All Cheatsheet Libraries cheatsheets

Kubernetes

kubectl command reference, core objects, and troubleshooting workflow for Kubernetes.

Kubernetes is a reconciliation loop, not a deployment tool

You declare the state you want. Controllers continuously compare that against reality and act to close the gap. Nothing is "deployed" so much as desired — which is why deleting a Pod directly does nothing useful: its controller simply recreates it.

Internalise that and most surprising behaviour stops being surprising. You almost never operate on Pods. You operate on the object that owns them.

Object What it is Notes
PodOne or more containers sharing a network namespace and storage.The smallest deployable unit. Never create one directly outside of debugging — it won't be rescheduled if the node dies.
ReplicaSetKeeps N identical Pods running.You rarely touch these directly; Deployments create and manage them for you.
DeploymentManages ReplicaSets to give you rolling updates and rollback.The default choice for any stateless workload.
StatefulSetPods with stable names, stable storage, and ordered startup.For databases and anything with identity. Pods get -0, -1 suffixes that survive rescheduling.
DaemonSetOne Pod per node, automatically.Log shippers, monitoring agents, CNI plugins.
Job / CronJobRun to completion, once or on a schedule.Set backoffLimit and history limits, or failed Jobs accumulate indefinitely.
ServiceA stable virtual IP and DNS name load-balancing to matching Pods.Selects Pods by label, not by owner. A typo in the selector yields a Service with no endpoints and no error.
IngressHTTP routing into the cluster, by host and path.Inert without an ingress controller installed. Increasingly superseded by the Gateway API.
ConfigMapNon-secret configuration as key/value pairs or files.Changes don't restart Pods automatically — see the Gotchas tab.
SecretSame shape as a ConfigMap, for sensitive values.Base64 is encoding, not encryption. Enable encryption at rest and RBAC, or use an external secrets store.
NamespaceA scope for names, quotas, and policy.Not a security boundary on its own — pair with NetworkPolicy and RBAC.
PV / PVCA piece of storage, and a claim on it.Most PVCs are filled dynamically by a StorageClass. Check the reclaim policy before deleting.
Labels are the wiring
  • Services, Deployments, and NetworkPolicies all find Pods by label selector. Labels are the only thing connecting them.
  • A Service whose selector matches nothing returns no error — it just has zero endpoints. kubectl get endpoints <svc> is the check.
  • Use the standard keys: app.kubernetes.io/name, /instance, /component, /part-of.
  • A Deployment's selector.matchLabels is immutable. Changing it means deleting and recreating the Deployment.
  • Annotations are for metadata and tooling; labels are for selection. Don't put large values in labels — they're indexed.
Group Command Does
Contextkubectl config get-contextsList every cluster you can reach.
Contextkubectl config use-context NAMESwitch clusters. Check this before every destructive command.
Contextkubectl config set-context --current --namespace=NSStop typing -n on every command.
Inspectkubectl get pods -o wideAdds node and IP columns — usually what you actually wanted.
Inspectkubectl get all -n NSCommon objects in a namespace. Note it omits ConfigMaps, Secrets, and Ingresses.
Inspectkubectl describe pod NAMEFull state plus the Events list at the bottom — the first place to look when something won't start.
Inspectkubectl get events --sort-by=.lastTimestampCluster-wide events in time order. Unsorted output is near-useless.
Inspectkubectl get po -o yamlThe full object as stored, including defaults the API server filled in.
Inspectkubectl explain deployment.spec.strategyBuilt-in schema docs for any field path. Faster than the website.
Logskubectl logs -f POD -c CONTAINERFollow logs from a specific container in a multi-container Pod.
Logskubectl logs POD --previousLogs from the crashed instance. Essential for CrashLoopBackOff.
Logskubectl logs -l app=api --tail=100Aggregate logs across every Pod matching a label.
Execkubectl exec -it POD -- shShell into a container. Use sh — many images have no bash.
Execkubectl debug POD -it --image=busybox --target=NAMEAttach an ephemeral debug container to a running Pod — works on distroless images with no shell.
Execkubectl port-forward svc/NAME 8080:80Tunnel a Service to localhost without exposing it.
Execkubectl cp POD:/path ./localCopy files out of a container. Requires tar in the image.
Deploykubectl apply -f dir/ --dry-run=serverValidate against the real API, including admission webhooks, without applying.
Deploykubectl diff -f manifest.yamlShows exactly what would change. Run this before every apply.
Deploykubectl rollout status deploy/NAMEBlocks until the rollout finishes or fails — the right CI gate.
Deploykubectl rollout undo deploy/NAMERoll back to the previous ReplicaSet. Add --to-revision=N to pick one.
Deploykubectl rollout restart deploy/NAMERolling restart with no manifest change — how you pick up a changed ConfigMap or Secret.
Scalekubectl scale deploy/NAME --replicas=3Immediate scale. Reverted on next apply if your manifest says otherwise.
Nodeskubectl top nodes / kubectl top podsLive CPU and memory. Needs metrics-server installed.
Nodeskubectl drain NODE --ignore-daemonsetsEvict Pods before maintenance. Respects PodDisruptionBudgets.
Nodeskubectl cordon NODE / uncordonStop or resume new scheduling on a node.
RBACkubectl auth can-i create pods -n NSTest your own permissions. Add --as=user to test someone else's.

A Deployment with the parts people leave out

The generated boilerplate works but omits everything that matters in production: resource bounds, health checks, and a security context. Those omissions are what cause the 3am pages.

apiVersion: apps/v1 kind: Deployment metadata: name: api labels: app.kubernetes.io/name: api spec: replicas: 3 selector: matchLabels: # immutable — you cannot change this later app.kubernetes.io/name: api template: metadata: labels: app.kubernetes.io/name: api spec: securityContext: runAsNonRoot: true runAsUser: 10001 seccompProfile: { type: RuntimeDefault } containers: - name: api image: registry.example.com/api:1.4.2 # pin a tag, never :latest ports: - containerPort: 8080 resources: requests: # what the scheduler reserves cpu: 100m memory: 128Mi limits: # memory over limit = OOMKilled memory: 256Mi livenessProbe: # failing = restart the container httpGet: { path: /healthz, port: 8080 } initialDelaySeconds: 10 periodSeconds: 10 readinessProbe: # failing = remove from Service endpoints httpGet: { path: /ready, port: 8080 } periodSeconds: 5 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: { drop: ["ALL"] } --- apiVersion: v1 kind: Service metadata: name: api spec: selector: app.kubernetes.io/name: api # must match the Pod labels above ports: - port: 80 targetPort: 8080
Requests and limits — the rule that matters
  • Requests are what the scheduler reserves. Limits are the hard ceiling.
  • Always set a memory limit. Exceeding it means the container is OOMKilled instantly — no grace, no signal.
  • Consider omitting CPU limits. CPU is compressible: hitting the limit throttles rather than kills, and aggressive limits cause latency that looks like an application bug. Setting requests without limits is a defensible, common choice.
  • Requests equal to limits gives a Pod Guaranteed QoS — evicted last under node pressure.
  • No requests at all gives BestEffort — evicted first. Fine for batch, wrong for anything serving traffic.
Probes
  • readiness — should traffic reach this Pod? Failing removes it from the Service, no restart.
  • liveness — is this container wedged? Failing restarts it. Too aggressive and you get restart loops under load.
  • startup — for slow starters. Suspends the other two until it passes; better than a huge initialDelaySeconds.
  • Don't point liveness at a check that touches the database. A slow dependency then restarts every Pod at once.
  • No readiness probe means traffic hits the Pod the instant the container starts, before it can serve.
Pod status Means Check
PendingNot scheduled to a node.describe events — usually insufficient CPU/memory, an unsatisfiable nodeSelector, or an unbound PVC.
ImagePullBackOffCan't fetch the image.Typo in the tag, private registry with no imagePullSecrets, or wrong architecture.
CrashLoopBackOffContainer starts then exits repeatedly.kubectl logs POD --previous. Usually a config error, a missing env var, or a failing liveness probe.
OOMKilledExceeded its memory limit.Raise the limit or fix the leak. Exit code 137. Visible in describe under Last State.
CreateContainerConfigErrorA referenced ConfigMap or Secret doesn't exist.Check the name and the namespace — they must be in the same namespace as the Pod.
Init:0/1Stuck in an init container.kubectl logs POD -c INIT_NAME. Often waiting on a dependency that never arrives.
Terminating (stuck)Won't finish deleting.Usually a finalizer. Inspect metadata.finalizers — force-deleting a StatefulSet Pod risks split-brain.
Running but not ReadyReadiness probe failing.The container is up but the app isn't serving. Check the probe path and port.

Debugging a Service that won't route

By far the most common "networking is broken" case, and it's nearly always labels.

1

Does the Service have endpoints?

kubectl get endpoints my-svc

Empty means the selector matches no ready Pods. That is the answer 80% of the time — either the labels don't match, or the Pods aren't passing readiness.

2

Compare the selector against actual Pod labels

kubectl get svc my-svc -o jsonpath='{.spec.selector}' kubectl get pods --show-labels
3

Check the ports

targetPort must match the container's actual listening port, not the Service's port. Mixing these up is extremely common.

4

Test from inside the cluster

kubectl run tmp --rm -it --image=nicolaka/netshoot -- bash # then: curl my-svc.my-namespace.svc.cluster.local nslookup my-svc

This separates a DNS problem from a routing problem from an application problem.

5

Check for a NetworkPolicy

If any NetworkPolicy selects the Pod, traffic is default-deny except what's explicitly allowed. A policy added elsewhere can silently break your Service.

Gotchas

ConfigMap and Secret changes don't restart anything
  • Updating a ConfigMap does not restart Pods. Values mounted as env vars keep the old value until the Pod restarts.
  • Volume-mounted ConfigMaps do update in place, but only after a sync delay — and only if the app re-reads the file.
  • Fix: kubectl rollout restart deploy/NAME, or hash the config into a Pod annotation so a change triggers a rollout automatically.
Secrets are barely secret by default
  • Base64 is encoding, not encryption. Anyone with read access to Secrets in the namespace has the plaintext.
  • Enable encryption at rest on etcd — it is not on by default in every distribution.
  • Lock down RBAC: get secrets is effectively credential access.
  • For anything serious use External Secrets Operator, Vault, or your cloud's secret manager.
  • Never commit a Secret manifest to git. Use Sealed Secrets or SOPS if it must live in a repo.
Other things that catch people
  • :latest is a trap. With imagePullPolicy: Always your Pods drift between nodes; without it, they never update. Pin a digest or an immutable tag.
  • kubectl delete pod on a managed Pod does nothing lasting — the controller recreates it. Scale the Deployment instead.
  • No PodDisruptionBudget means a node drain can take your whole service down at once.
  • Namespaces don't isolate network traffic. Any Pod can reach any other Pod unless a NetworkPolicy says otherwise, and many CNI plugins ignore them entirely.
  • kubectl apply on a resource created with create can conflict over ownership. Stay consistent.
  • An unset terminationGracePeriodSeconds defaults to 30s — long-draining apps get SIGKILLed mid-request.
  • StatefulSet PVCs are not deleted when the StatefulSet is. That's deliberate, and it surprises people at bill time.

Tips

kubectl diff before every apply

Shows precisely what would change against the live cluster. The single cheapest habit for avoiding accidental production changes.

Alias and autocomplete

alias k=kubectl plus source <(kubectl completion bash) and complete -o default -F __start_kubectl k. Add kubectx/kubens for fast context and namespace switching.

kubectl explain

Schema docs for any field path, straight from your cluster's actual API version — so it's never out of date the way a web page can be. Add --recursive for the whole tree.

netshoot for network debugging

kubectl run tmp --rm -it --image=nicolaka/netshoot -- bash gives you dig, curl, tcpdump, and iproute2 inside the cluster network.

--dry-run=server

Validates against the real API including admission webhooks and defaulting, catching things client-side validation misses entirely.

Generate manifests, don't hand-write them

kubectl create deploy api --image=x --dry-run=client -o yaml scaffolds valid YAML you then edit — much faster than remembering the nesting.

Resources