Workloads
Pod
The smallest deployable unit in Kubernetes — one or more containers that share a network namespace and storage. You rarely create Pods directly; a controller makes them for you.
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
💡 Containers in the same Pod reach each other on localhost and share volumes.
Deployment
The standard way to run stateless apps. It manages a ReplicaSet and gives you rolling updates, rollbacks, and a declared replica count.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels: { app: web }
template:
metadata:
labels: { app: web }
spec:
containers:
- name: web
image: nginx:1.27
💡 Change the image and Kubernetes rolls Pods one batch at a time with zero downtime.
ReplicaSet
Keeps a fixed number of identical Pods running. Deployments create and manage ReplicaSets for you — you almost never touch one directly.
kubectl get rs
# web-7d9f8c 3 3 3 2m
💡 Each rollout of a Deployment creates a new ReplicaSet; old ones are kept for rollback.
StatefulSet
Like a Deployment but for stateful apps that need stable network identities and stable per-Pod storage — databases, queues, clustered services.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: db
spec:
serviceName: db
replicas: 3
selector:
matchLabels: { app: db }
template:
metadata:
labels: { app: db }
spec:
containers:
- name: db
image: postgres:16
💡 Pods get ordinal names (db-0, db-1, db-2) and keep them across reschedules.
DaemonSet
Runs exactly one copy of a Pod on every node (or a subset). Used for node-level agents — log collectors, metrics exporters, CNI plugins.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-agent
spec:
selector:
matchLabels: { app: log-agent }
template:
metadata:
labels: { app: log-agent }
spec:
containers:
- name: agent
image: fluent-bit:3.1
💡 Add a node and a DaemonSet Pod appears on it automatically.
Job
Runs a Pod to completion — a batch task that should run once, succeed, and stop. Kubernetes retries it until the configured number of completions succeed.
apiVersion: batch/v1
kind: Job
metadata:
name: migrate
spec:
backoffLimit: 4
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: myapp:1.0
command: ["./migrate.sh"]
💡 Use for migrations, one-off imports, and processing tasks.
CronJob
Creates Jobs on a cron schedule. The Kubernetes equivalent of crontab — for backups, reports, and periodic cleanup.
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-backup
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: backup-tool:2.0
💡 Schedule uses standard cron syntax in the cluster timezone (UTC by default).
Networking
Service
A stable network endpoint in front of a set of Pods. Pods come and go, but the Service gives them one steady virtual IP and DNS name.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector: { app: web }
ports:
- port: 80
targetPort: 8080
💡 The selector matches Pod labels; traffic is load-balanced across matching Pods.
ClusterIP
The default Service type. Gives the Service an internal-only virtual IP reachable from inside the cluster — not from the outside world.
spec:
type: ClusterIP # default, can be omitted
ports:
- port: 80
💡 Reach it by DNS name: <service>.<namespace>.svc.cluster.local.
NodePort
A Service that opens the same high port (30000–32767) on every node. External traffic hitting any node IP on that port reaches the Service.
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30080
💡 Crude but dependency-free. Usually fronted by an external load balancer in production.
LoadBalancer
A Service that asks the cloud provider to provision an external load balancer with a public IP pointing at the Service.
spec:
type: LoadBalancer
ports:
- port: 443
targetPort: 8443
💡 Only works where the cloud controller supports it (AWS, GCP, Azure, etc.).
Ingress
HTTP/HTTPS routing rules — host- and path-based — that send external traffic to Services. Needs an Ingress controller (nginx, Traefik) to do the actual work.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port: { number: 80 }
💡 One external IP can serve many domains and paths through a single Ingress controller.
NetworkPolicy
A firewall for Pods. Declares which Pods may talk to which, by label and port. Without one, all Pods can reach all others.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-allow-api
spec:
podSelector:
matchLabels: { app: db }
ingress:
- from:
- podSelector:
matchLabels: { app: api }
💡 Only enforced if the CNI plugin supports it (Calico, Cilium); flannel does not.
Configuration
ConfigMap
Stores non-secret configuration as key/value pairs. Mount it as files or inject it as environment variables — keeps config out of the image.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: "info"
feature.flags: "beta=on"
💡 Updating a mounted ConfigMap eventually updates the files; env vars need a Pod restart.
Secret
Like a ConfigMap but for sensitive data — passwords, tokens, TLS keys. Stored base64-encoded and handled with extra care by the API.
apiVersion: v1
kind: Secret
metadata:
name: db-creds
type: Opaque
stringData:
password: s3cr3t
💡 Base64 is encoding, not encryption — enable encryption-at-rest and RBAC to protect Secrets.
Namespace
A virtual cluster inside a cluster. Groups resources, scopes names, and is the boundary for quotas and RBAC.
kubectl create namespace staging
kubectl get pods -n staging
💡 Default objects live in the "default" namespace; system components live in "kube-system".
Storage
PersistentVolume (PV)
A piece of cluster storage provisioned by an admin or a StorageClass. It exists independently of any Pod that uses it.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-data
spec:
capacity: { storage: 10Gi }
accessModes: ["ReadWriteOnce"]
hostPath: { path: /mnt/data }
💡 A PV is the actual storage; a PVC is the request to use it.
PersistentVolumeClaim (PVC)
A Pod's request for storage — "I need 10Gi, ReadWriteOnce". Kubernetes binds it to a matching PV or provisions one dynamically.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests: { storage: 10Gi }
💡 Pods mount the PVC, not the PV directly — this decouples apps from storage details.
StorageClass
A template for dynamically provisioning PVs. Defines the provisioner (e.g. an EBS or GCE PD driver) and parameters like disk type.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: ebs.csi.aws.com
parameters:
type: gp3
💡 A PVC referencing a StorageClass triggers on-demand disk creation — no manual PV needed.
Volume
A directory accessible to containers in a Pod. Many types exist — configMap, secret, emptyDir, persistentVolumeClaim, hostPath.
spec:
volumes:
- name: cache
emptyDir: {}
containers:
- name: app
volumeMounts:
- name: cache
mountPath: /cache
💡 A Volume's lifetime is tied to the Pod (except PVC-backed volumes, which persist).
emptyDir
A scratch volume created empty when a Pod starts and deleted when the Pod is removed. Good for caches and sharing files between containers in one Pod.
volumes:
- name: tmp
emptyDir:
sizeLimit: 500Mi
💡 Set medium: Memory to back it with RAM (tmpfs) for speed.
kubectl verbs
get
List resources. The first command you reach for. Add -o wide, -o yaml, or -o json for more detail.
kubectl get pods -n prod -o wide
kubectl get deploy,svc,ingress
💡 Add --watch (-w) to stream changes live.
describe
Show a detailed, human-readable dump of one resource — including recent Events, which is where you find why a Pod won't start.
kubectl describe pod web-7d9f8c-abc12
💡 The Events section at the bottom is the single most useful debugging output.
apply
Create or update resources from a manifest, declaratively. Kubernetes diffs your file against the live state and reconciles.
kubectl apply -f deployment.yaml
kubectl apply -k ./overlays/prod
💡 Prefer apply over create — it is idempotent and tracks the last-applied config.
delete
Remove resources by name, label, or file. Deleting a Deployment also removes its Pods.
kubectl delete pod web-7d9f8c-abc12
kubectl delete -f deployment.yaml
💡 Add --grace-period=0 --force to kill a stuck Pod immediately (use sparingly).
exec
Run a command inside a running container — usually an interactive shell for debugging.
kubectl exec -it web-7d9f8c-abc12 -- sh
💡 The -- separates kubectl flags from the command you want to run in the container.
logs
Print a container's stdout/stderr. Add -f to follow, --previous to see the crashed container's logs.
kubectl logs -f web-7d9f8c-abc12
kubectl logs web-7d9f8c-abc12 --previous
💡 For multi-container Pods, pick one with -c <container>.
port-forward
Tunnel a local port to a Pod or Service port. Lets you hit an internal service from your laptop without exposing it.
kubectl port-forward svc/web 8080:80
💡 Great for debugging databases and dashboards that have no public endpoint.
scale
Change the replica count of a Deployment, ReplicaSet, or StatefulSet imperatively.
kubectl scale deployment web --replicas=5
💡 Manual scaling is overridden by an HPA if one targets the same workload.
rollout
Manage and inspect rolling updates — check status, pause, resume, undo to a previous revision, or restart Pods.
kubectl rollout status deployment/web
kubectl rollout undo deployment/web
kubectl rollout restart deployment/web
💡 rollout restart cycles Pods without changing the spec — handy to reload config or Secrets.
edit
Open a live resource in your editor and apply changes on save. Quick fixes, but changes are not tracked in your manifests.
kubectl edit deployment web
💡 Convenient for hotfixes; for anything lasting, edit the YAML and apply it.
Resources & scheduling
requests
The amount of CPU/memory a container is guaranteed. The scheduler uses requests to decide which node a Pod fits on.
resources:
requests:
cpu: "250m"
memory: "256Mi"
💡 250m means 0.25 of a CPU core; memory is in bytes with Ki/Mi/Gi suffixes.
limits
The hard cap on CPU/memory a container may use. Exceeding the memory limit gets the container OOM-killed; CPU is throttled, not killed.
resources:
limits:
cpu: "500m"
memory: "512Mi"
💡 A frequent cause of mysterious restarts is hitting the memory limit (OOMKilled).
QoS classes
Quality of Service tiers Kubernetes assigns from your requests/limits: Guaranteed (limits == requests on all containers), Burstable (some requests set), BestEffort (none set).
# Guaranteed:
resources:
requests: { cpu: "500m", memory: "512Mi" }
limits: { cpu: "500m", memory: "512Mi" }
💡 Under node pressure, BestEffort Pods are evicted first, Guaranteed last.
HPA (HorizontalPodAutoscaler)
Automatically scales the replica count of a workload up or down based on metrics like CPU utilisation or custom metrics.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target: { type: Utilization, averageUtilization: 70 }
💡 Requires the metrics-server and CPU/memory requests set on the Pods.
node
A worker machine (VM or physical) that runs Pods. Each node runs the kubelet and a container runtime.
kubectl get nodes -o wide
kubectl describe node ip-10-0-1-23
💡 Control-plane nodes run the API server, scheduler, and etcd; worker nodes run your Pods.
label
A key/value tag attached to objects. The glue of Kubernetes — Services, selectors, and tooling all match on labels.
metadata:
labels:
app: web
tier: frontend
env: prod
💡 kubectl get pods -l app=web,env=prod filters by label.
selector
A query over labels. Services, Deployments, and NetworkPolicies use selectors to find the Pods they apply to.
selector:
matchLabels:
app: web
💡 A Service's selector must match its Pods' labels or it routes to nothing.
annotation
A key/value note attached to an object for tools and humans — not used for selection. Holds metadata like ingress config or change cause.
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
kubernetes.io/change-cause: "bump to v1.27"
💡 Unlike labels, annotations are not indexed and can hold large or structured values.
taint
A mark on a node that repels Pods. Only Pods with a matching toleration may schedule there. Used to reserve nodes (e.g. GPU nodes).
kubectl taint nodes gpu-1 \
dedicated=gpu:NoSchedule
💡 Effects: NoSchedule (block new), PreferNoSchedule (avoid), NoExecute (also evict existing).
toleration
A Pod's permission to schedule onto a node with a matching taint. Tolerations do not force placement — they only allow it.
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
💡 Pair with nodeSelector or affinity to actually pull the Pod onto the tainted node.