Cheatsheet Kubernetes
Orquestração de contentores para deploy, escala e gestão automatizada
Kubernetes
Cluster and Context
Cluster Info
kubectl cluster-info kubectl cluster-info dump kubectl get nodes kubectl get nodes -o wide kubectl top nodes kubectl version --short
cluster-info shows the API server address and services. get nodes lists the cluster nodes with their state (Ready, NotReady). top nodes shows CPU/memory usage (requires metrics-server).
Node Status
kubectl get nodes -o wide kubectl describe node worker-1 kubectl get nodes --show-labels kubectl get nodes -l kubernetes.io/the=linux kubectl cordon worker-1 kubectl uncordon worker-1
describe node shows capacity, conditions and scheduled pods. cordon marks the node the unavailable for new pods (maintenance). uncordon reverts it. Labels such the kubernetes.io/the allow filtering nodes.
Labels and Selectors
kubectl get pods -l app=web kubectl get pods -l 'env in (prod,staging)' kubectl get pods -l app=web,tier=frontend kubectl label pod my-pod tier=frontend kubectl label pod my-pod tier- kubectl get pods --show-labels
Labels are key/value pairs that identify resources. -l filters by selector. label pod tier=frontend adds; tier- removes. --show-labels shows all the labels of each resource.
Namespaces
kubectl get namespaces kubectl get ns kubectl create namespace dev kubectl create namespace staging kubectl get pods -n dev kubectl get pods --all-namespaces kubectl get pods -A
Namespaces logically isolate resources within the same cluster. -n or --namespace specifies the target namespace. -A or --all-namespaces lists resources from all namespaces.
Drain a Node
kubectl drain worker-1 \ --ignore-daemonsets \ --delete-emptydir-data kubectl drain worker-1 --force kubectl uncordon worker-1
drain evacuates a node: terminates pods gracefully and prevents new scheduling. --ignore-daemonsets is needed because DaemonSets cannot be evacuated. After maintenance, uncordon re-enables the node.
Annotations
kubectl annotate pod my-pod \
description="Test pod"
kubectl annotate pod my-pod description-
kubectl get pod my-pod -o jsonpath=\
'{.metadata.annotations}'Annotations store non-identifying metadata (descriptions, URLs, contacts). Unlike labels, they are not used for selection. annotate key- removes the annotation.
Contexts and kubeconfig
kubectl config get-contexts kubectl config current-context kubectl config use-context prod-cluster kubectl config set-context --current \ --namespace=dev kubectl config view
A context combines cluster + user + namespace. use-context switches cluster/environment. set-context --current --namespace sets the default namespace of the current context. The ~/.kube/config file stores everything.
System Components
kubectl get componentstatuses kubectl get pods -n kube-system kubectl get pods -n kube-system -l \ k8s-app=kube-dns kubectl logs -n kube-system \ -l k8s-app=kube-dns
The kube-system namespace contains the core components: API server, etcd, scheduler, controller-manager and CoreDNS. Checking these pods is the first step when diagnosing cluster problems.
Delete Resources
kubectl delete pod my-pod kubectl delete pods --all -n dev kubectl delete deployment web kubectl delete -f manifest.yaml kubectl delete pods -l app=legacy kubectl delete pod my-pod \ --grace-period=0 --force
delete removes resources by name, label (-l) or file (-f). --grace-period=0 --force forces immediate removal (useful for pods stuck in Terminating). Careful with --all.
Explore the API
kubectl api-resources kubectl api-resources --namespaced=true kubectl api-versions kubectl explain pod kubectl explain pod.spec.containers kubectl explain deployment.spec --recursive
api-resources lists all available resource types with their shortnames. explain shows a field documentation directly in the terminal — useful for exploring the structure of any object without leaving the CLI.
Formatted Output
kubectl get pods -o wide
kubectl get pods -o yaml
kubectl get pods -o json
kubectl get pod my-pod -o jsonpath=\
'{.status.podIP}'
kubectl get pods -o custom-columns=\
NAME:.metadata.name,STATUS:.status.phase-o controls the output format: wide (more columns), yaml, json. jsonpath extracts specific fields. custom-columns creates custom tables with the fields you want.
Dry-run and Generate YAML
kubectl run nginx --image=nginx \ --dry-run=client -o yaml > pod.yaml kubectl create deployment web \ --image=nginx --replicas=3 \ --dry-run=client -o yaml > deploy.yaml kubectl apply -f pod.yaml --dry-run=server
--dry-run=client simulates without creating and -o yaml generates the manifest — ideal for creating templates. --dry-run=server validates against the API server without persisting. Saves time and avoids YAML syntax errors.
Pods
List Pods
kubectl get pods kubectl get pods -o wide kubectl get pods -A kubectl get pods -w kubectl get pods --field-selector \ status.phase=Running kubectl get pods --sort-by=.status.startTime
get pods lists the pods of the current namespace. -w (watch) updates in real time. --field-selector filters by fields such the status.phase. -o wide adds each pod's IP and node.
Run Commands
kubectl exec -it my-pod -- sh kubectl exec -it my-pod -- bash kubectl exec my-pod -- ls /app kubectl exec my-pod -- cat /etc/config/app.yaml kubectl exec -it my-pod -c sidecar -- sh
exec runs commands inside a running container. -it gives an interactive terminal. Everything after -- is the command. -c selects the container in pods with multiple ones. Essential for inspecting the inside of pods.
Multi-container Pods
spec:
containers:
- name: app
image: myapp:1.0
- name: sidecar
image: fluentd:latest
initContainers:
- name: init-db
image: busybox
command: ['sh', '-c', 'sleep 5']A pod can have several containers that share network and volumes. initContainers run sequentially before the main ones (setup, migrations). Common patterns: sidecar (logging, proxy) and ambassador (network proxy).
Create a Pod Imperatively
kubectl run nginx --image=nginx kubectl run app --image=myapp:1.0 \ --port=8080 kubectl run test --image=busybox \ --rm -it -- sh kubectl run debug --image=nicolaka/netshoot \ --rm -it -- bash
run creates a pod directly. --rm -it creates an ephemeral pod with an interactive terminal that deletes itself on exit — perfect for debugging. --port exposes the container port (does not create a Service).
Port-forward
kubectl port-forward my-pod 8080:80 kubectl port-forward svc/web 8080:80 kubectl port-forward deployment/web 8080:80 kubectl port-forward my-pod 8080:80 \ --address=0.0.0.0
port-forward creates a local tunnel to a pod, Service or Deployment. 8080:80 maps local port 8080 to port 80 of the pod. Ideal for testing without creating a Service. --address=0.0.0.0 allows external access.
Probes (Health Checks)
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /ready
port: 8080livenessProbe restarts the container if it fails (stuck app). readinessProbe removes the pod from the Service if it is not ready (still starting up). Types: httpGet, tcpSocket, exec. initialDelaySeconds gives startup time.
Create a Pod Declaratively
apiVersion: v1
kind: Pod
metadata:
name: my-pod
labels:
app: web
spec:
containers:
- name: app
image: nginx:1.25
ports:
- containerPort: 80A Pod is the smallest schedulable unit. spec.containers defines the containers (there can be several). containerPort is informational — real exposure is done via a Service. Apply with kubectl apply -f pod.yaml.
Describe a Pod
kubectl describe pod my-pod kubectl get pod my-pod -o yaml kubectl get events --field-selector \ involvedObject.name=my-pod
describe shows full details: spec, state, conditions and Events at the end. The events reveal scheduling errors, image pull issues or failed probes. It is the first command for diagnosing a troubled pod.
Copy Files
kubectl cp my-pod:/app/log.txt ./log.txt kubectl cp ./config.yaml my-pod:/etc/config/ kubectl cp my-pod:/data ./backup \ -c sidecar
cp copies files between the local system and a pod (like docker cp). Format: pod:/path for remote. -c specifies the container. Useful for extracting logs, configs or injecting debug files.
Logs
kubectl logs my-pod kubectl logs -f my-pod kubectl logs my-pod --tail=100 kubectl logs my-pod --since=1h kubectl logs my-pod -c sidecar kubectl logs my-pod --previous
logs shows the container output. -f follows in real time (like tail -f). -c specifies the container in multi-container pods. --previous shows the logs of the previous container (after a crash/restart).
Pod States
Pending # waiting for scheduling/pull Running # running Succeeded # finished successfully Failed # finished with an error Unknown # undetermined state CrashLoopBackOff # restarting in a loop ImagePullBackOff # failed to pull the image
Pending indicates a lack of resources or an image being downloaded. CrashLoopBackOff means the container keeps terminating repeatedly. ImagePullBackOff indicates an image error (wrong name, private registry, missing credentials).
Delete Pods
kubectl delete pod my-pod kubectl delete pods --all kubectl delete pods -l app=legacy kubectl delete pod my-pod \ --grace-period=0 --force kubectl delete pod my-pod --wait=false
Pods managed by Deployments are automatically recreated when deleted. --grace-period=0 --force forces immediate removal (stuck pods). --wait=false does not wait for termination. To stop for good, delete the Deployment.
Deployments e Rollouts
Create a Deployment
kubectl create deployment web \ --image=nginx --replicas=3 kubectl apply -f deployment.yaml kubectl get deployments kubectl get rs
A Deployment manages ReplicaSets that keep the desired number of pods. create deployment is imperative; apply -f is declarative (preferred). get rs shows the created ReplicaSets.
Pause and Resume a Rollout
kubectl rollout pause deployment/web kubectl set image deployment/web \ nginx=nginx:1.26 kubectl set resources deployment/web \ --limits=cpu=500m,memory=256Mi kubectl rollout resume deployment/web
pause freezes the rollout to apply multiple changes at once. On resume, Kubernetes performs a single rollout with all the changes — avoids multiple intermediate rollouts and reduces downtime.
Restart a Deployment
kubectl rollout restart deployment/web kubectl rollout status deployment/web
rollout restart restarts all pods in a controlled way (rolling update) without changing the image. Useful after updating a mounted ConfigMap or Secret, or to clear in-memory state of the containers.
Deployment Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25spec.selector.matchLabels must match the template.metadata.labels — the Deployment uses that to know which pods to manage. template is the pod template the ReplicaSet replicates.
RollingUpdate Strategy
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
spec:
strategy:
type: RecreateRollingUpdate replaces pods gradually (default). maxSurge = extra pods during the update; maxUnavailable: 0 guarantees zero-downtime. Recreate terminates all before creating new ones (there is downtime).
Revision History Limit
spec: revisionHistoryLimit: 5 progressDeadlineSeconds: 300
revisionHistoryLimit controls how many old revisions to keep (default: 10). progressDeadlineSeconds sets the timeout for the rollout to be considered failed if it does not progress. Adjust to the speed of your deploys.
Update the Image
kubectl set image deployment/web \ nginx=nginx:1.26 kubectl apply -f deployment.yaml kubectl rollout status deployment/web kubectl rollout status deployment/web \ --timeout=60s
set image updates a container image without editing YAML. The format is container=new-image. rollout status tracks the update progress until all pods are ready.
ReplicaSet
kubectl get rs kubectl get rs -l app=web kubectl describe rs web-abc123 kubectl scale rs web-abc123 --replicas=5 kubectl delete rs web-abc123
The ReplicaSet ensures that N pods with the right labels are always running. It is created automatically by the Deployment. It is rarely created directly — always use a Deployment to get rollouts and history.
MinReadySeconds
spec:
minReadySeconds: 10
template:
spec:
containers:
- name: app
image: myapp:1.0minReadySeconds defines how long a pod must be Ready before being considered available. It prevents the rollout from advancing too fast if the app takes time to stabilize after the readiness probe passes.
Rollback
kubectl rollout history deployment/web kubectl rollout history deployment/web \ --revision=2 kubectl rollout undo deployment/web kubectl rollout undo deployment/web \ --to-revision=2
rollout undo reverts to the previous revision. --to-revision=N reverts to a specific revision. rollout history lists the revisions. By default, Kubernetes keeps 10 revisions (revisionHistoryLimit).
Scale a Deployment
kubectl scale deployment web --replicas=5
kubectl scale deployment web --replicas=0
kubectl get deployment web -o jsonpath=\
'{.spec.replicas}'scale adjusts the number of replicas immediately. --replicas=0 pauses the app without deleting the Deployment (all pods terminate). Useful for staging environments that do not need to be always active.
Inspect a Deployment
kubectl get deployment web -o yaml
kubectl describe deployment web
kubectl get deployment web -o jsonpath=\
'{.status.readyReplicas}'
kubectl get deployment web -o jsonpath=\
'{.status.conditions[*].type}'describe shows replicas, conditions and events. status.readyReplicas indicates how many pods are ready. conditions reveals Available, Progressing and ReplicaFailure for quick diagnosis.
Services e Networking
Service Types
ClusterIP # internal (default) NodePort # port on each node LoadBalancer # external LB (cloud) ExternalName # DNS CNAME kubectl get svc kubectl get svc -o wide
ClusterIP exposes only inside the cluster (default). NodePort opens a port (30000-32767) on all nodes. LoadBalancer provisions an external balancer (AWS, GCP, Azure). ExternalName creates a CNAME to an external DNS.
Internal DNS
# Format: # <svc>.<namespace>.svc.cluster.local curl http://web.default.svc.cluster.local nslookup web.default # Same namespace: curl http://web
Each Service gets a DNS record: <name>.<namespace>.svc.cluster.local. Within the same namespace, the name is enough (http://web). CoreDNS in kube-system resolves these names automatically.
Service Without a Selector
apiVersion: v1 kind: Service metadata: name: external-db spec: ports: - port: 5432 --- apiVersion: v1 kind: Endpoints metadata: name: external-db subsets: - addresses: - ip: 10.0.0.50 ports: - port: 5432
A Service without a selector does not create Endpoints automatically. You can create manual Endpoints pointing to external IPs — useful for databases outside the cluster, keeping the same internal DNS pattern.
Create a Service Imperatively
kubectl expose deployment web \ --port=80 --target-port=8080 kubectl expose deployment web \ --port=80 --type=NodePort kubectl expose pod my-pod --port=8080
expose creates a Service from a Deployment or Pod. --port is the Service port; --target-port is the container port. The selector is automatically inherited from the exposed resource labels.
Headless Service
spec:
clusterIP: None
selector:
app: web
ports:
- port: 80A Headless Service (clusterIP: None) has no virtual IP. DNS returns the pod IPs directly. Used with StatefulSets to give each pod a stable DNS name (pod-0.svc.ns) or for manual service discovery.
Session Affinity
spec:
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 3600sessionAffinity: ClientIP routes requests from the same IP always to the same pod (sticky sessions). Default is None (round-robin). Useful for apps with in-memory state, but prefer stateless + external cache.
Service Manifest
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIPThe Service selector must match the labels of the target pods. port is the Service port; targetPort is the container port. The Service distributes traffic across the pods matching the selector (internal load balancing).
Endpoints
kubectl get endpoints web kubectl describe endpoints web kubectl get endpointslice
Endpoints lists the IPs of the pods the Service is routing to. If it is empty, the selector matches no pods or the pods are not Ready. EndpointSlice is the scalable version for large clusters.
Inspect Services
kubectl get svc -o wide
kubectl describe svc web
kubectl get svc web -o jsonpath=\
'{.spec.clusterIP}'
kubectl get svc -Adescribe svc shows selector, ports, endpoints and type. clusterIP is the internal virtual IP. If the Endpoints are empty, check whether the pod labels match the Service selector.
NodePort
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30080
kubectl get svc web -o jsonpath=\
'{.spec.ports[0].nodePort}'NodePort exposes the Service on a fixed port (30000-32767) of all nodes. Accessible via node-IP:nodePort. If nodePort is not specified, Kubernetes assigns one automatically. Good for dev/testing.
Multi-port Service
spec:
ports:
- name: http
port: 80
targetPort: 8080
- name: metrics
port: 9090
targetPort: 9090A Service can expose multiple ports. Each port needs a name when there is more than one. Useful for exposing the app (80) and metrics (9090) on the same Service. The Ingress references the port by name.
Basic NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080NetworkPolicy controls traffic between pods (L3/L4 firewall). podSelector defines the affected pods. ingress.from specifies who can communicate. Requires a CNI that supports NetworkPolicy (Calico, Cilium).
ConfigMaps e Secrets
Create a ConfigMap
kubectl create configmap app-config \ --from-file=config.yaml kubectl create configmap vars \ --from-literal=ENV=prod \ --from-literal=DEBUG=false kubectl get configmaps
ConfigMap stores non-sensitive configuration in key/value pairs. --from-file loads files; --from-literal sets values inline. Each key becomes available the an environment variable or mounted file.
Create a Secret
kubectl create secret generic db-pass \ --from-literal=password=s3cret kubectl create secret docker-registry \ regcred --docker-server=registry.io \ --docker-user=admin --docker-password=pass kubectl get secrets
Secret stores sensitive data (passwords, tokens, keys). Values are encoded in base64 (not encrypted by default). docker-registry creates credentials for private registries. Enable etcd encryption in production.
ImagePullSecrets
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app
image: registry.io/myapp:1.0imagePullSecrets provides credentials for pulling images from private registries. Create it with kubectl create secret docker-registry. You can configure it on the ServiceAccount to apply to all pods in the namespace.
ConfigMap Manifest
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
ENV: "production"
LOG_LEVEL: "info"
app.properties: |
server.port=8080
server.host=0.0.0.0data accepts simple key/value pairs or full files (with | for multi-line). Keys with a dot (e.g. app.properties) are treated the file names when mounted the a volume.
Secret Manifest
apiVersion: v1 kind: Secret metadata: name: db-creds type: Opaque stringData: username: admin password: s3cret # or with base64: data: password: czNjcmV0
stringData accepts plain-text values (Kubernetes encodes them). data requires values already in base64. type: Opaque is the default; special types: kubernetes.io/tls, kubernetes.io/dockerconfigjson.
Edit ConfigMap/Secret
kubectl edit configmap app-config kubectl edit secret db-creds kubectl create configmap vars \ --from-literal=ENV=staging \ --dry-run=client -o yaml | kubectl apply -f -
edit opens the editor to modify directly. The --dry-run=client -o yaml | kubectl apply -f - pattern recreates the resource with new values (useful in CI/CD). Mounted volumes update; env vars require a pod restart.
ConfigMap the Env
env:
- name: ENV
valueFrom:
configMapKeyRef:
name: app-config
key: ENV
envFrom:
- configMapRef:
name: app-configconfigMapKeyRef injects a specific key the a variable. envFrom.configMapRef injects all the ConfigMap keys the environment variables (key name = variable name). Simpler for configs with many keys.
Secret the Env
env:
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-creds
key: password
envFrom:
- secretRef:
name: db-credssecretKeyRef injects a Secret key the a variable. envFrom.secretRef injects all the keys. The value is decoded automatically. The app sees plain text — base64 is only transport/storage.
ConfigMap vs Secret
# ConfigMap: non-sensitive data # - configs, URLs, flags # - plain text in etcd # Secret: sensitive data # - passwords, tokens, keys # - base64 in etcd (enable encryption) kubectl get cm app-config -o yaml kubectl get secret db-creds -o yaml
Use ConfigMap for non-sensitive configuration and Secret for credentials. Both have a 1 MiB limit. Secrets are base64 (not encrypted by default) — enable EncryptionConfiguration in etcd in production.
ConfigMap the Volume
volumes:
- name: config-vol
configMap:
name: app-config
volumeMounts:
- name: config-vol
mountPath: /etc/config
readOnly: trueMounting a ConfigMap the a volume creates one file per key in mountPath. Updates to the ConfigMap propagate automatically (with a delay). readOnly: true prevents accidental writes.
Secret the Volume
volumes:
- name: secret-vol
secret:
secretName: db-creds
defaultMode: 0400
volumeMounts:
- name: secret-vol
mountPath: /etc/secrets
readOnly: trueMounting a Secret the a volume creates files with restricted permissions. defaultMode: 0400 guarantees read only by the owner. Updates propagate automatically. Prefer volumes over env vars for large secrets.
Immutable ConfigMap/Secret
apiVersion: v1 kind: ConfigMap metadata: name: app-config immutable: true data: ENV: "production"
immutable: true prevents changes after creation (improves cluster performance — the kubelet does not watch). To update, delete and recreate. Ideal for versioned configs that change via a new Deployment.
Storage e Volumes
PersistentVolumeClaim
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standardA PVC requests storage from the cluster. accessModes: ReadWriteOnce (1 node), ReadOnlyMany (several nodes, read), ReadWriteMany (several nodes, write). The StorageClass defines the provisioner.
View Volumes and Classes
kubectl get pv kubectl get pvc kubectl get storageclass kubectl get sc kubectl describe pvc data-pvc kubectl get pvc -o wide
get pv shows the cluster volumes (global). get pvc shows claims per namespace. States: Bound (attached), Pending (waiting). describe pvc reveals provisioning events and bind errors.
Expand a Volume
kubectl edit pvc data-pvc # Change: storage: 10Gi -> 20Gi kubectl get pvc data-pvc
Expansion requires allowVolumeExpansion: true on the StorageClass. Edit the PVC to increase the size. Some provisioners expand online; others require a pod restart. Shrinking is not supported.
Use a PVC in a Pod
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc
containers:
- name: app
volumeMounts:
- name: data
mountPath: /var/dataThe pod references the PVC by name in volumes and mounts it at mountPath. The data persists even if the pod is deleted or recreated. The PVC must be in the same namespace the the pod.
emptyDir
volumes:
- name: cache
emptyDir: {}
- name: shared
emptyDir:
medium: Memory
sizeLimit: 256MiemptyDir creates a temporary volume that lives the long the the pod exists. Ideal for shared cache between containers of the same pod. medium: Memory uses RAM (tmpfs) for performance. Data is lost when the pod is deleted.
Volume subPath
volumeMounts: - name: config-vol mountPath: /etc/app/config.yaml subPath: config.yaml - name: data mountPath: /data/user1 subPath: user1
subPath mounts only one file/subdirectory of the volume instead of the whole volume. Useful for mounting a single file from a ConfigMap without replacing the entire directory. It does not auto-update with ConfigMaps.
PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs
spec:
capacity:
storage: 50Gi
accessModes:
- ReadWriteMany
nfs:
server: 10.0.0.10
path: /exports/dataA PV is the actual storage resource (NFS, iSCSI, cloud disk). It can be provisioned statically (admin creates it) or dynamically (via StorageClass). The PVC binds to a compatible PV automatically.
hostPath
volumes:
- name: logs
hostPath:
path: /var/log/app
type: DirectoryOrCreatehostPath mounts a node directory into the pod. The data persists on the node but does not migrate with the pod. type: DirectoryOrCreate creates it if it does not exist. Use with caution — it couples the pod to a specific node (avoid in production).
StorageClass
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: fast-ssd provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd reclaimPolicy: Delete volumeBindingMode: WaitForFirstConsumer
StorageClass enables dynamic provisioning: when a PVC is created, the PV is created automatically. provisioner defines the plugin (cloud, NFS, local). WaitForFirstConsumer delays creation until a pod uses the PVC.
Reclaim Policies
# Retain: PV keeps data after PVC deletion
# Delete: PV and data are deleted
# Recycle: data deleted, PV reusable
kubectl patch pv pv-nfs -p \
'{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'reclaimPolicy defines what happens to the PV when the PVC is deleted. Retain preserves data (safe). Delete removes everything (cloud default). Recycle is deprecated. Configure it on the StorageClass or PV.
Scale and Resources
Scale Manually
kubectl scale deployment web --replicas=5 kubectl scale deployment web --replicas=0 kubectl get deployment web kubectl get pods -l app=web
scale adjusts replicas immediately. The ReplicaSet creates or terminates pods to reach the desired count. --replicas=0 stops the app without deleting the Deployment. Scaling down terminates pods (graceful shutdown).
ResourceQuota
apiVersion: v1
kind: ResourceQuota
metadata:
name: quota-dev
namespace: dev
spec:
hard:
pods: "20"
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16GiResourceQuota limits the total resources per namespace. It prevents a team from exhausting AS cluster. If the quota is reached, new pods are rejected. Combine it with LimitRange for per-pod defaults.
Pod Disruption Budget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: webThe PDB guarantees minimum availability during voluntary disruptions (drain, upgrade). minAvailable: 2 ensures at least 2 active pods. Alternative: maxUnavailable: 1. Protects against downtime during maintenance.
HPA (Horizontal Pod Autoscaler)
kubectl autoscale deployment web \ --min=2 --max=10 --cpu-percent=80 kubectl get hpa kubectl describe hpa web kubectl delete hpa web
The HPA adjusts replicas automatically based on metrics. --cpu-percent=80 scales when the average CPU exceeds 80%. It requires metrics-server and requests defined on the pods to calculate percentages.
LimitRange
apiVersion: v1
kind: LimitRange
metadata:
name: defaults
spec:
limits:
- default:
cpu: "500m"
memory: "256Mi"
defaultRequest:
cpu: "100m"
memory: "64Mi"
type: ContainerLimitRange defines requests/limits defaults for containers without explicit values. It can also enforce min/max per container. Applied per namespace. Ensures no pod is left without defined limits.
Topology Spread
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: webtopologySpreadConstraints distributes pods evenly across nodes, zones or regions. maxSkew: 1 allows a maximum difference of 1 pod between topologies. DoNotSchedule rejects if it cannot distribute. Ensures high availability.
Declarative HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 75autoscaling/v2 supports multiple metrics (CPU, memory, custom). scaleTargetRef points to the Deployment. averageUtilization is a percentage of the request. The HPA checks metrics every 15-30 seconds.
VPA (Vertical Pod Autoscaler)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: web-vpa
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: web
updatePolicy:
updateMode: "Auto"The VPA adjusts requests/limits automatically based on real usage. updateMode: Auto restarts pods with new values. Off only recommends. Do not use VPA and HPA on CPU at the same time (conflict). Requires extra installation.
Node Affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values: [ssd]nodeAffinity controls which nodes the pods are scheduled on. required is mandatory; preferred is soft (weight). More flexible than a simple nodeSelector. Use it for SSDs, GPUs or specific zones.
Requests and Limits
resources:
requests:
cpu: "250m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"requests = guaranteed resources (used for scheduling). limits = maximum allowed. CPU in millicores (250m = 0.25 core). Memory: Mi (mebibytes). Exceeding the memory limit causes OOMKilled; CPU is throttled.
Priority and Preemption
apiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: high-priority value: 1000000 globalDefault: false description: "Critical pods" --- spec: priorityClassName: high-priority
PriorityClass defines a numeric priority. Pods with higher priority can preempt (terminate) lower-priority pods to obtain resources. Essential for critical workloads in shared clusters.
Pod Affinity/Anti-affinity
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: web
topologyKey: kubernetes.io/hostnamepodAntiAffinity spreads pods of the same app across different nodes (HA). podAffinity groups related pods on the same node (local cache). topologyKey defines the domain: hostname, zone or region.
Advanced
Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80Ingress routes external HTTP/HTTPS traffic to internal Services. host defines the domain; path defines the route. Requires an Ingress Controller (nginx, traefik, AWS ALB). Supports TLS, rewrites and rate limiting.
ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
---
spec:
serviceAccountName: app-sa
containers:
- name: app
image: myapp:1.0A ServiceAccount gives pods an identity to communicate with the API. Each pod uses the default SA if none is specified. Create dedicated SAs with minimal RBAC. The token is automatically mounted at /var/run/secrets/.
StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres-headless
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: db
image: postgres:15
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10GiA StatefulSet is for stateful apps (DBs, queues). It guarantees: stable names (postgres-0, postgres-1), individual DNS, persistent storage per pod and ordered startup/shutdown. Requires a Headless Service.
Ingress with TLS
spec:
tls:
- hosts:
- app.example.com
secretName: tls-secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80tls enables HTTPS on the Ingress. secretName references a Secret of type kubernetes.io/tls with tls.crt and tls.key. Combine with cert-manager for automatic certificates via Let's Encrypt.
Jobs
apiVersion: batch/v1
kind: Job
metadata:
name: backup
spec:
completions: 1
backoffLimit: 3
template:
spec:
containers:
- name: backup
image: busybox
command: ["sh", "-c", "echo backup done"]
restartPolicy: NeverA Job runs a task until it completes. completions = successful runs required. backoffLimit = retries before failing. restartPolicy: Never or OnFailure (never Always). Ideal for migrations and backups.
NetworkPolicy (egress)
spec:
podSelector:
matchLabels:
app: web
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: database
ports:
- port: 5432policyTypes: Egress controls outgoing traffic. This example only allows the web pod to communicate with port 5432 in the database namespace. Without a NetworkPolicy, all traffic is allowed by default (zero-trust).
RBAC (Role and RoleBinding)
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: reader namespace: dev rules: - apiGroups: [""] resources: ["pods", "services"] verbs: ["get", "list", "watch"] --- kind: RoleBinding metadata: name: reader-binding subjects: - kind: User name: dev roleRef: kind: Role name: reader
A Role defines permissions (resources + verbs) within a namespace. A RoleBinding binds the Role to users/groups/ServiceAccounts. ClusterRole/ClusterRoleBinding are global. Principle of least privilege.
CronJobs
apiVersion: batch/v1
kind: CronJob
metadata:
name: daily-backup
spec:
schedule: "0 2 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
template:
spec:
containers:
- name: backup
image: busybox
command: ["sh", "-c", "echo backup"]
restartPolicy: OnFailureA CronJob creates Jobs on a schedule (cron format). concurrencyPolicy: Allow, Forbid (no overlap), Replace (cancels the previous one). successfulJobsHistoryLimit controls how many completed Jobs to keep.
Pod Security Standards
metadata:
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restrictedPod Security Standards labels on the namespace: privileged (no restrictions), baseline (minimum), restricted (maximum). enforce blocks, audit logs, warn warns. Replaces the deprecated PodSecurityPolicy.
Imperative RBAC
kubectl create role reader \ --verb=get,list,watch \ --resource=pods,services -n dev kubectl create rolebinding reader-bind \ --role=reader --user=dev -n dev kubectl auth can-i get pods -n dev kubectl auth can-i delete pods --the=dev
create role and create rolebinding create permissions via the CLI. auth can-i checks whether an action is allowed. --the=dev impersonates another user. Essential for validating permissions before granting access.
DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-agent
spec:
selector:
matchLabels:
app: log-agent
template:
metadata:
labels:
app: log-agent
spec:
containers:
- name: fluentd
image: fluentd:latestA DaemonSet runs exactly 1 pod on every node (or a subset via nodeSelector). When a new node joins, the pod is created automatically. Used for logging (fluentd), monitoring (prometheus-node-exporter) and networking (CNI).
kubectl debug
kubectl debug my-pod -it --image=busybox kubectl debug my-pod --image=nicolaka/netshoot \ --target=app kubectl debug node/worker-1 -it \ --image=ubuntu kubectl debug deployment/web \ --image=curlimages/curl
debug creates an ephemeral container in an existing pod (no restart). --target shares the target container's network namespace. debug node/ creates a privileged pod on the node. Ideal for diagnosing pods without debug tools.
Manifests YAML
Basic Structure
apiVersion: v1
kind: Pod
metadata:
name: my-pod
labels:
app: web
annotations:
team: backend
spec:
containers:
- name: app
image: nginxEvery manifest has 4 required fields: apiVersion (API version), kind (resource type), metadata (name, labels) and spec (desired state). metadata.name must be unique within the namespace.
Labels and Selectors in Manifests
metadata:
labels:
app: web
version: v1
tier: frontend
spec:
selector:
matchLabels:
app: web
matchExpressions:
- key: version
operator: In
values: [v1, v2]matchLabels requires an exact match. matchExpressions supports operators: In, NotIn, Exists, DoesNotExist. The Deployment selector is immutable after creation — plan your labels well.
Security Context
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]securityContext defines privileges: runAsNonRoot prevents root, readOnlyRootFilesystem prevents writes, drop: ALL removes Linux capabilities. Essential for hardening in production.
Multi-document YAML
apiVersion: v1 kind: ConfigMap metadata: name: config data: ENV: prod --- apiVersion: v1 kind: Service metadata: name: web spec: ports: - port: 80
--- separates multiple resources in a single file. kubectl apply -f processes them all sequentially. Ideal for grouping related resources (Deployment + Service + ConfigMap) in one versioned file.
Environment Variables
spec:
containers:
- name: app
env:
- name: DB_HOST
value: "postgres.default"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NODE_IP
valueFrom:
fieldRef:
fieldPath: status.hostIPenv defines environment variables. fieldRef injects pod metadata (name, IP, namespace) — called the Downward API. Useful for logging, service discovery and pod identification without hardcoding.
Tolerations and Taints
# Taint on the node:
kubectl taint nodes worker-1 gpu=true:NoSchedule
# Toleration on the pod:
spec:
tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"Taints repel pods from nodes. Tolerations allow a pod to tolerate the taint. Effects: NoSchedule (does not schedule), PreferNoSchedule (avoids), NoExecute (evicts). Use for dedicated nodes (GPU, maintenance).
Apply vs Create
kubectl apply -f manifest.yaml kubectl create -f manifest.yaml kubectl apply -f ./manifests/ kubectl apply -f https://url/manifest.yaml kubectl diff -f manifest.yaml
apply is declarative and idempotent: it creates or updates (merge). create fails if the resource already exists. apply -f ./dir/ applies every YAML in the directory. diff shows what would change without applying.
Commands and Args
spec:
containers:
- name: app
command: ["sh", "-c"]
args: ["echo $ENV && sleep 3600"]
env:
- name: ENV
value: "production"command overrides the Dockerfile ENTRYPOINT. args overrides the CMD. Both accept arrays. Environment variables defined in env are expanded in args. Essential for init scripts and debugging.
Owner References and GC
kubectl get rs -o yaml | grep ownerReferences -A5 kubectl delete deployment web \ --cascade=foreground kubectl delete deployment web \ --cascade=orphan
ownerReferences link resources together (Deployment → ReplicaSet → Pod). Deleting the owner deletes the dependents (garbage collection). --cascade=orphan removes the owner but keeps the pods. foreground waits for the dependents to be deleted.
Kustomize
# kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - deployment.yaml - service.yaml namePrefix: prod- replicas: - name: web count: 5 commonLabels: env: production
Kustomize customizes manifests without templates. namePrefix, replicas, patches and commonLabels transform the base. Built into kubectl: kubectl apply -k ./overlays/prod/. An alternative to Helm.
Lifecycle Hooks
lifecycle:
postStart:
exec:
command: ["sh", "-c", "echo ready"]
preStop:
exec:
command: ["sh", "-c", "sleep 10"]postStart runs after the container starts (no ordering guarantee with ENTRYPOINT). preStop runs before termination — ideal for graceful shutdown (deregister from the LB, drain connections). The pod waits for preStop to complete.
Validate Manifests
kubectl apply -f manifest.yaml --dry-run=client kubectl apply -f manifest.yaml --dry-run=server kubectl apply -f manifest.yaml --validate=strict kubeconform -f manifest.yaml kubeval manifest.yaml
--dry-run=client validates syntax locally. --dry-run=server validates against the API server (more complete). --validate=strict rejects unknown fields. External tools: kubeconform, kubeval, datree.
Tips and Troubleshooting
Cluster Events
kubectl get events --sort-by=\ .metadata.creationTimestamp kubectl get events -n dev --field-selector \ type=Warning kubectl get events -A --watch
get events shows recent events (pod creation, pull failures, OOM). --sort-by orders chronologically. type=Warning filters errors only. Events expire after ~1 hour — use centralized logging for history.
Backup and Restore (etcd)
kubectl get all -A -o yaml > backup-all.yaml kubectl get deployment,svc,cm,secret \ -n prod -o yaml > prod-backup.yaml kubectl apply -f prod-backup.yaml # etcd snapshot (admin): ETCDCTL_API=3 etcdctl snapshot save /backup.db
Export resources with -o yaml for a declarative backup. get all does not include everything (PVCs, Secrets, Roles are missing). For a full cluster backup, snapshot etcd. Tools: Velero, kube-backup.
JSONPath and Custom Output
kubectl get pods -o jsonpath=\
'{.items[*].metadata.name}'
kubectl get pods -o jsonpath=\
'{.items[?(@.status.phase=="Running")].metadata.name}'
kubectl get nodes -o jsonpath=\
'{range .items[*]}{.metadata.name}{"\t"}{.status.conditions[-1].type}{"\n"}{end}'jsonpath extracts specific data from the JSON. .items[*] iterates arrays. ?(@.field=="value") filters. range/end creates loops with formatting. Combine with xargs for script automation.
Namespaces the Environments
kubectl create namespace dev kubectl create namespace staging kubectl create namespace prod kubectl config set-context dev-ctx \ --cluster=my-cluster --namespace=dev kubectl get pods -n prod kubectl top pods -n staging
Use namespaces to isolate environments (dev, staging, prod) or teams. Combine with ResourceQuota to limit resources and NetworkPolicy to isolate traffic. Contexts with a default namespace avoid the constant -n.
Useful Aliases
alias k='kubectl' alias kgp='kubectl get pods' alias kgs='kubectl get svc' alias kgd='kubectl get deployments' alias kaf='kubectl apply -f' alias kdp='kubectl describe pod' alias kl='kubectl logs -f' alias kex='kubectl exec -it' # Autocomplete: source <(kubectl completion bash) complete -o default -F __start_kubectl k
Aliases speed up daily kubectl usage. completion bash enables autocomplete for resources and names. Add them to .bashrc or .zshrc. Tools like kubectx and kubens streamline context/namespace switching.
Production Best Practices
# 1. Always define requests/limits # 2. Use liveness + readiness probes # 3. runAsNonRoot: true # 4. readOnlyRootFilesystem: true # 5. Images with a specific tag (not :latest) # 6. PodDisruptionBudget for HA # 7. topologySpreadConstraints # 8. Encrypted Secrets (etcd encryption)
Production requires: requests/limits (avoids noisy neighbors), probes (health checks), a restrictive securityContext, fixed image tags (reproducibility), PDB (availability during upgrades) and topologySpread (HA).
Debugging Problematic Pods
kubectl get pods kubectl describe pod my-pod kubectl logs my-pod --previous kubectl exec -it my-pod -- sh kubectl get events --field-selector \ involvedObject.name=my-pod kubectl top pod my-pod
Debug flow: 1) get pods (status), 2) describe (events), 3) logs --previous (previous crash), 4) exec (inside), 5) top (resources). CrashLoopBackOff → check logs; Pending → check resources/nodes.
Essential Tools
# Management: kubectl, kubectx, kubens, k9s # Debug: kubectl debug, stern (logs), ktop # Security: kube-bench, trivy, falco, opa # Deploy: helm, kustomize, argocd, flux # Monitoring: prometheus, grafana, loki
Essential ecosystem: k9s (TUI), stern (multi-pod logs), helm (package manager), argocd (GitOps), prometheus+grafana (monitoring), trivy (image scanning), kube-bench (CIS benchmarks).