# Kubernetes Cheatsheet

- URL: https://karandeepsingh.ca/cheatsheets/containers/kubernetes/
- Author: Karandeep Singh
- Published: 2025-05-27
- Tags: kubernetes, containers
- Summary: A working kubectl reference with core resource YAML skeletons, rollout and scaling commands, debugging workflows, and a table of handy one-liners.

I spend a good chunk of every week inside kubectl and still blank on flag combinations at the worst moments, so this page is my working memory. Commands, YAML skeletons, and the debugging moves I actually use on real clusters. Everything here assumes your images are already built and pushed; my [Docker Cheatsheet](https://karandeepsingh.ca/cheatsheets/containers/docker/) covers that half of the workflow.

## kubectl Basics

> **Docs:** [kubectl reference on kubernetes.io](https://kubernetes.io/docs/reference/kubectl/)

```bash
kubectl get pods                          # Pods in current namespace
kubectl get pods -n kube-system           # Specific namespace
kubectl get pods -A                       # All namespaces
kubectl get pods -o wide                  # Node, IP, readiness detail
kubectl get deploy web -o yaml            # Full live manifest
kubectl describe pod web-7d4b9c6f5-x2k8j  # Events, conditions, mounts
kubectl logs web-7d4b9c6f5-x2k8j          # Container logs
kubectl logs -f deploy/web                # Follow logs via the Deployment
kubectl logs web-... -c sidecar --previous   # Prior crashed container
kubectl exec -it web-... -- sh            # Shell into a pod
kubectl apply -f manifest.yaml            # Create/update declaratively
kubectl apply -f k8s/ -R                  # Whole directory, recursive
kubectl delete -f manifest.yaml
kubectl delete pod web-... --grace-period=0 --force   # Last resort
kubectl diff -f manifest.yaml             # Preview what apply would change
```

| Flag | Meaning |
|------|---------|
| `-n <ns>` | Target a namespace |
| `-A` | All namespaces |
| `-o wide` | Extra columns (node, IP) |
| `-o yaml` / `-o json` | Full object output |
| `-o jsonpath='{...}'` | Extract specific fields |
| `-l app=web` | Filter by label selector |
| `--watch` / `-w` | Stream changes live |
| `--dry-run=client -o yaml` | Generate YAML without creating |

## Context & Namespace Switching

> **Docs:** [Kubernetes docs on configuring access to multiple clusters](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/)

```bash
kubectl config get-contexts               # List clusters/contexts
kubectl config current-context
kubectl config use-context prod-cluster
kubectl config set-context --current --namespace=payments   # Default ns
```

I alias these constantly. `kubectx` and `kubens` do the same job with less typing if you can install them.

## Core Resource YAML Skeletons

> **Docs:** [Kubernetes Deployment docs](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)

### Deployment

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: registry.example.com/team/web:1.4.2
          ports:
            - containerPort: 8080
          envFrom:
            - configMapRef:
                name: web-config
            - secretRef:
                name: web-secrets
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              memory: 256Mi
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
```

### Service

```yaml
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: ClusterIP        # NodePort / LoadBalancer for external
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080
```

### ConfigMap & Secret

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
data:
  LOG_LEVEL: info
  FEATURE_FLAGS: "beta-ui,new-checkout"
---
apiVersion: v1
kind: Secret
metadata:
  name: web-secrets
type: Opaque
stringData:            # stringData = plain text; k8s base64-encodes it
  DATABASE_URL: postgres://app:secret@db:5432/app
```

```bash
kubectl create secret generic web-secrets \
  --from-literal=DATABASE_URL=postgres://... --dry-run=client -o yaml
```

### Ingress

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts: [app.example.com]
      secretName: web-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80
```

## Rollouts

> **Docs:** [Kubernetes docs on updating and rolling back Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)

```bash
kubectl rollout status deploy/web         # Block until rollout completes
kubectl rollout history deploy/web        # Revision list
kubectl rollout history deploy/web --revision=3   # Details of one revision
kubectl rollout undo deploy/web           # Roll back to previous
kubectl rollout undo deploy/web --to-revision=2
kubectl rollout restart deploy/web        # Rolling restart (picks up config changes)
kubectl set image deploy/web web=registry.example.com/team/web:1.4.3
```

## Scaling & Autoscaling

> **Docs:** [Horizontal Pod Autoscaling on kubernetes.io](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/)

```bash
kubectl scale deploy/web --replicas=5
kubectl autoscale deploy/web --min=3 --max=10 --cpu-percent=70
kubectl get hpa
kubectl top pods                          # Needs metrics-server
kubectl top nodes
```

`kubectl autoscale` generates an `autoscaling/v2` HorizontalPodAutoscaler behind the scenes. Export it with `kubectl get hpa web -o yaml` if you want to commit it declaratively (that is where memory and custom-metric targets go too).

## Port-Forward

> **Docs:** [Kubernetes port-forwarding docs](https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/)

```bash
kubectl port-forward svc/web 8080:80              # localhost:8080 → service:80
kubectl port-forward deploy/web 8080:8080
kubectl port-forward pod/db-0 5432:5432 -n data   # Handy for DB clients
```

## Debugging

> **Docs:** [Monitoring, logging, and debugging on kubernetes.io](https://kubernetes.io/docs/tasks/debug/)

```bash
kubectl get events --sort-by=.lastTimestamp       # Recent cluster events
kubectl get events --field-selector involvedObject.name=web-7d4b9c6f5-x2k8j
kubectl describe pod web-...                      # Events section at the bottom
kubectl debug -it web-... --image=busybox --target=web   # Ephemeral container
kubectl debug node/worker-3 -it --image=ubuntu    # Debug pod on a node
kubectl get pod web-... -o jsonpath='{.status.containerStatuses[0].lastState}'
```

### CrashLoopBackOff Triage

1. `kubectl logs web-... --previous`. The crash reason is almost always here.
2. `kubectl describe pod web-...`, then check Events for OOMKilled, failed probes, image pull errors.
3. Exit code 137 → OOMKilled: raise memory limits or fix the leak.
4. Failing liveness probe → the app is slow to start; raise `initialDelaySeconds` or switch to a startup probe.
5. Still stuck? Override the entrypoint to keep the pod alive and poke around: `kubectl debug` with an ephemeral container, or set `command: ["sleep", "3600"]` in a copy of the pod.

**tip:**
`ImagePullBackOff` is nearly always one of three things: a typo in the image tag, a missing `imagePullSecrets` entry for a private registry, or the image genuinely never got pushed. Check `kubectl describe pod`; the event text tells you which.

## Labels & Selectors

> **Docs:** [Kubernetes labels and selectors docs](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)

```bash
kubectl get pods -l app=web                       # Equality selector
kubectl get pods -l 'env in (staging,prod)'       # Set-based selector
kubectl get pods -l app=web,tier!=cache
kubectl label pod web-... debug=true              # Add a label
kubectl label pod web-... debug-                  # Remove it
kubectl get pods --show-labels
```

Services, Deployments, HPAs, and NetworkPolicies all match pods by labels. A Service with `selector: app: web` routes to every ready pod carrying that label, no matter what created it.

## Resource Requests & Limits

**warning:**
A pod with a memory **limit** and no **request** gets a request equal to the
limit, and a pod with neither lands in `BestEffort` — the first thing evicted
when a node is under pressure. A CPU limit throttles rather than kills, which
shows up as latency nobody can explain.

```diff
# deployment.yaml
@@ -14,6 +14,12 @@ spec:
         image: myapi:1.4.2
+        resources:
+          requests:          # what the scheduler reserves
+            cpu: 100m
+            memory: 128Mi
+          limits:            # memory: hard kill. cpu: throttle.
+            memory: 256Mi
```

> **Docs:** [Kubernetes docs on resource management for Pods and containers](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/)

| Setting | Effect |
|---------|--------|
| `requests.cpu` / `requests.memory` | What the scheduler reserves; pod won't schedule without capacity |
| `limits.cpu` | Throttled above this (no kill) |
| `limits.memory` | OOM-killed above this (exit 137) |

My defaults: always set requests (scheduling accuracy), set a memory limit (protect neighbours), and think twice before CPU limits, because throttling shows up as mystery latency.

## Handy One-Liners

> **Docs:** [kubectl quick reference on kubernetes.io](https://kubernetes.io/docs/reference/kubectl/quick-reference/)

| Task | Command |
|------|---------|
| Generate Deployment YAML | `kubectl create deploy web --image=nginx --dry-run=client -o yaml` |
| Throwaway debug pod | `kubectl run tmp --rm -it --image=busybox -- sh` |
| Decode a secret | `kubectl get secret web-secrets -o jsonpath='{.data.DATABASE_URL}' \| base64 -d` |
| Pods on one node | `kubectl get pods -A --field-selector spec.nodeName=worker-3` |
| Restart counts | `kubectl get pods -o custom-columns=NAME:.metadata.name,RESTARTS:.status.containerStatuses[0].restartCount` |
| All images running in cluster | `kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.containers[*].image}{"\n"}{end}' \| sort -u` |
| Drain a node | `kubectl drain worker-3 --ignore-daemonsets --delete-emptydir-data` |
| Uncordon it after | `kubectl uncordon worker-3` |
| Delete all Evicted pods | `kubectl delete pods -A --field-selector status.phase=Failed` |
| Watch a rollout live | `kubectl get pods -l app=web -w` |

Most of my controllers and operators are written in Go. The client-go ecosystem is a big reason I keep my [Go (Golang) Cheatsheet](https://karandeepsingh.ca/cheatsheets/languages/golang/) nearby when working against the Kubernetes API.

**Question:** Pick a running deployment. Does it set memory requests — and if not, do you know which pods your cluster evicts first when a node fills up?

## Related Cheatsheets

- [Docker](https://karandeepsingh.ca/cheatsheets/containers/docker/): multi-stage builds, image tagging, volumes, and compose. Every Pod spec on this page starts there
- [Go (Golang)](https://karandeepsingh.ca/cheatsheets/languages/golang/): the language kubectl and most operators are written in. Toolchain, goroutines, channels, and table-driven tests
- [AWS CloudFormation](https://karandeepsingh.ca/cheatsheets/devops/cloudformation/): templates, change sets, and drift detection for the VPCs and cluster infrastructure underneath these workloads

More container pages sit in the [Containers group](https://karandeepsingh.ca/cheatsheets/containers/); the full index is the [cheatsheet library](https://karandeepsingh.ca/cheatsheets/).
