../

kubectl commands

The exact kubectl command and flag for each task, grouped the way you work at a terminal, for kubectl v1.37. What the objects are and why they behave as they do is in Kubernetes; chart releases are driven with Helm commands.

Setup

PlatformInstall
macOSbrew install kubectl
Windowswinget install -e --id Kubernetes.kubectl
Debian, Ubuntu, Fedorathe pkgs.k8s.io package repository, one per minor version (see the install guide)
Snapsudo snap install kubectl --classic
Any Linuxthe static binary from dl.k8s.io, below
v=$(curl -Ls https://dl.k8s.io/release/stable.txt)
u="https://dl.k8s.io/release/$v/bin/linux/amd64"
curl -LO "$u/kubectl" && curl -LO "$u/kubectl.sha256"
echo "$(cat kubectl.sha256)  kubectl" | sha256sum -c
sudo install -m 0755 kubectl /usr/local/bin/kubectl

Swap amd64 for arm64 on ARM, or $v for a fixed version such as v1.37.2.

CommandShows
kubectl versionclient and server versions; warns when they are more than one minor apart
kubectl version --clientclient only, no cluster needed
kubectl cluster-infoAPI server and CoreDNS addresses
kubectl get --raw='/readyz?verbose'API server health checks, one per line
kubectl api-versionsevery group/version the server serves

Version skew: kubectl is supported within one minor version of the API server, older or newer, so a v1.37 client covers 1.36 to 1.38 control planes.

Kubeconfig and contexts

A kubeconfig file lists clusters (API URL and CA), users (credentials) and contexts (a cluster, a user and a default namespace), plus the current-context.

~/.kube/config clusters contexts users kind-dev https://127.0.0.1:6443 + CA certificate prod https://k8s.acme.dev + CA certificate kind-dev client cert + key prod-admin exec: aws eks get-token dev cluster: kind-dev user: kind-dev namespace: web prod cluster: prod user: prod-admin namespace: default current-context: prod kubectl get pods → https://k8s.acme.dev as prod-admin, in default --context dev, -n kube-system: change them for one command only
A context names a cluster, a user and a namespace; current-context picks the one kubectl uses.
SourceUsed when
--kubeconfig ./ci.yamlgiven: only that file
KUBECONFIG=~/.kube/a:~/.kube/bset: files merged, the first file to set a value wins (; on Windows)
~/.kube/configotherwise
CommandDoes
kubectl config get-contextslist contexts, * marks the current one
kubectl config current-contextprint its name
kubectl config use-context prodswitch
kubectl config set-context --current --namespace=webchange the default namespace
kubectl config view --minify -o jsonpath='{..namespace}'the current namespace (empty means default)
kubectl --context prod -n web get podsone-off, without switching
kubectl config view --minifythe current context only; --raw also prints keys and tokens
kubectl config view --minify --flatten > one.yamla self-contained file for one context
KUBECONFIG=a.yaml:b.yaml kubectl config view --flatten > merged.yamlmerge files
kubectl config set-context dev --cluster=kind-dev --user=kind-dev --namespace=webadd a context
kubectl config rename-context gke_acme_europe-west1_prod prodshorter name
kubectl config delete-context oldalso delete-cluster, delete-user
kubectl config get-clusters, get-userslist the other two sections
ClusterWrite credentials into your kubeconfig
EKSaws eks update-kubeconfig --name prod --region eu-west-1
GKEgcloud container clusters get-credentials prod --region europe-west1
AKSaz aks get-credentials --resource-group rg --name prod
kindkind export kubeconfig --name dev
k3scopy /etc/rancher/k3s/k3s.yaml from the server and fix its server: address

kubectx and kubens

Install with brew install kubectx or kubectl krew install ctx ns (then kubectl ctx, kubectl ns). With fzf installed, running either without arguments opens a picker.

CommandDoes
kubectxlist contexts
kubectx prodswitch
kubectx -back to the previous context
kubectx -cprint the current one
kubectx prod=gke_acme_europe-west1_prodrename
kubectx -d olddelete
kubens web, kubens -, kubens -cthe same for the namespace

Command anatomy

kubectl  get   deploy/api   -n web -o yaml
         verb  TYPE/NAME    flags
FormExample
TYPEkubectl get pods
TYPE NAME…kubectl get pod api-7d9f web-5c2b
TYPE/NAME…kubectl get deploy/api svc/api
TYPE1,TYPE2kubectl get deploy,svc,cm -l app=api
TYPE.GROUPkubectl get certificates.cert-manager.io: disambiguates kinds with the same name
TYPE.VERSION.GROUPkubectl get deployments.v1.apps
-f FILE, a directory, a URL or -kubectl get -f k8s/
-k DIRobjects from a kustomization
Global flagDoes
-n web, -Anamespace, all namespaces
--context prod, --kubeconfig fwhich cluster and credentials
--as jane, --as-group devsimpersonate (needs the impersonate permission)
--request-timeout=10sgive up on slow API calls
-v=6log request URLs and status codes; -v=8 adds bodies, -v=9 everything
kubectl optionslist every global flag

Short names

ShortKindShortKind
popodsdeploydeployments
rsreplicasetsstsstatefulsets
dsdaemonsetscjcronjobs
svcservicesingingresses
ependpoints (prefer endpointslices)netpolnetworkpolicies
cmconfigmapssaserviceaccounts
pvpersistentvolumespvcpersistentvolumeclaims
scstorageclassesnsnamespaces
nonodesevevents
hpahorizontalpodautoscalerspdbpoddisruptionbudgets
crdcustomresourcedefinitionspcpriorityclasses
quotaresourcequotaslimitslimitranges
csrcertificatesigningrequests

Secrets, Jobs, Roles and RoleBindings have no short name.

Discovering kinds and fields

CommandShows
kubectl api-resourcesevery kind: short names, API group, namespaced or not
kubectl api-resources --namespaced=falsecluster-scoped kinds only
kubectl api-resources --api-group=appsone group
kubectl api-resources --verbs=list -o namenames only, for scripts
kubectl explain pod.spec.containersfield docs for any path
kubectl explain deploy.spec --recursive --max-depth=2the field tree, two levels deep
kubectl explain httproute --api-version=gateway.networking.k8s.io/v1a specific version of a kind

Reading resources

CommandDoes
kubectl get podsthe current namespace
kubectl get pods -Aevery namespace
kubectl get pods -o wideadds node, Pod IP, nominated node
kubectl get pods -wprint changes as they happen; --watch-only skips the initial list
kubectl get pod api-7d9f -o yamlthe full object; managedFields hidden unless --show-managed-fields
kubectl get pods --show-labels, -L app,versionlabels, all or as columns
kubectl get pods --no-headersno header row, for scripts
kubectl get pod api-7d9f --ignore-not-foundexit 0 and print nothing if it's missing
kubectl get deploy api --subresource=statusone subresource (status, scale)
kubectl get all -n webworkloads and Services only: no ConfigMaps, Secrets, Ingresses, PVCs or custom resources
kubectl get --raw /api/v1/namespaces/web/podsthe raw API response
kubectl describe pod api-7d9fhuman summary: status, conditions, mounts, recent events
kubectl describe deploy -l app=apievery match

Output formats

FlagPrints
-o wideextra columns
-o yaml, -o jsonwhole objects
-o kyamlKYAML: a stricter YAML style (quoted strings, {} and []) that avoids type surprises such as no becoming false
-o namedeployment.apps/api, ready for xargs
-o jsonpath='…', -o jsonpath-file=fchosen fields
-o custom-columns=NAME:.metadata.name,NODE:.spec.nodeNameyour own table; custom-columns-file=f
-o go-template='…', -o go-template-file=fGo templates; base64decode is available
--sort-by=.metadata.creationTimestampsort by any field (JSONPath without braces)
JSONPathSelects
{.metadata.name}one field
{.items[*].metadata.name}that field from every item, space-separated
{.items[0]}, {.items[-1:]}first, last item
{range .items[*]}{.metadata.name}{"\n"}{end}one line per item
{.items[?(@.status.phase=="Running")].metadata.name}filter by a field
{.status.conditions[?(@.type=="Ready")].status}one condition
{..image}every image at any depth (includes status copies)
{.metadata.labels.app\.kubernetes\.io/name}a key with dots: escape them
{.items[*]['metadata.name', 'spec.nodeName']}several fields

JSONPath has no regular expressions: pipe -o json into jq for those. On Windows, wrap the template in double quotes and use single quotes inside it.

# name and node of every Pod, tab-separated
t='{range .items[*]}{.metadata.name}{"\t"}'
t+='{.spec.nodeName}{"\n"}{end}'
kubectl get pods -o jsonpath="$t"
 
# Pods with their image(s), as a table
kubectl get pods -o custom-columns=\
'POD:.metadata.name,IMAGE:.spec.containers[*].image'

Selectors

FlagMatches
-l app=apilabel equals (== works too)
-l 'tier!=db'not equal, or label absent
-l 'env in (prod,staging)', -l 'env notin (dev)'set membership
-l app, -l '!canary'label exists, doesn't exist
-l app=api,tier=webcomma means AND
--field-selector status.phase=Failedserver-side field filter
--field-selector spec.nodeName=worker-2Pods on one node
--field-selector metadata.name=api,metadata.namespace!=devmetadata.name and metadata.namespace work on every kind

Pod field selectors also accept spec.restartPolicy, spec.serviceAccountName, status.podIP; Events accept involvedObject.name, involvedObject.kind, reason, type.

Creating and changing

apply and diff

CommandDoes
kubectl apply -f app.yamlcreate or update; client-side apply keeps a last-applied-configuration annotation
kubectl apply -f k8s/ -Ra directory tree
kubectl apply -k overlays/proda kustomization
kubectl apply -f https://example.com/x.yamlfrom a URL
kubectl apply --server-side -f app.yamlserver-side apply: the API server merges and records field owners
--field-manager=cithe owner name recorded for your fields (SSA default kubectl)
--force-conflictstake fields another manager owns
--dry-run=serverfull validation and admission, nothing stored
--dry-run=clientlocal only, no cluster checks
--validate=strictthe default: unknown or duplicate fields fail; warn, ignore
kubectl apply --prune -l app=api -f k8s/also delete labeled objects no longer in the files; easy to get wrong, test with --dry-run=server
kubectl diff -f k8s/what apply would change; exit 0 none, 1 changes, above 1 error
kubectl diff --server-side -f k8s/the same, computed as server-side apply
KUBECTL_EXTERNAL_DIFF="colordiff -N -u" kubectl diff -f k8s/your own diff tool
last-applied annotation: last file image: api:1.4.2 replicas: 3 env DEBUG: "1" your file applied now image: api:1.4.3 replicas: 3 live object in the cluster image: api:1.4.2 replicas: 5 env DEBUG: "1" restartedAt: 09:30 in last-applied, not in file file differs from live delete set live after kubectl apply image: api:1.4.3 replicas: 3 the scale to 5 is undone env DEBUG removed restartedAt kept in neither file --server-side records field owners in managedFields instead
apply sets what your file says, deletes only what you removed from it, and leaves fields it never set alone.

Generators: create and run

Every create and run accepts --dry-run=client -o yaml, which prints the object instead of creating it: the fastest way to start a manifest.

kubectl create deploy api --image=ghcr.io/acme/api:1.4.2 \
  --replicas=3 --port=3000 --dry-run=client -o yaml \
  > deploy.yaml
kubectl create service clusterip api --tcp=80:3000 \
  --dry-run=client -o yaml > svc.yaml
kubectl run tmp --image=busybox:1.37 --restart=Never \
  --dry-run=client -o yaml -- sleep 3600 > pod.yaml
CommandCreates
kubectl create deploy api --image=ghcr.io/acme/api:1.4.2 --replicas=3 --port=3000Deployment (labels app=api)
kubectl expose deploy api --port=80 --target-port=3000ClusterIP Service for an existing workload; --type=LoadBalancer
kubectl create service nodeport api --tcp=80:3000Service with selector app=api
kubectl create ingress web --class=traefik --rule="shop.example.com/*=web:80"Ingress; /* is a Prefix path, add ,tls=web-tls for TLS
kubectl create job migrate --image=ghcr.io/acme/api:1.4.2 -- bun run db:migrateJob
kubectl create job report-now --from=cronjob/reportrun a CronJob now
kubectl create cronjob report --image=ghcr.io/acme/jobs:1.4.2 --schedule="30 2 * * *"CronJob
kubectl run tmp --image=busybox:1.37 --restart=Never -- sleep 3600bare Pod
kubectl create ns webNamespace
kubectl create sa api -n webServiceAccount
kubectl create role pod-reader --verb=get,list,watch --resource=podsRole
kubectl create rolebinding api-pods --role=pod-reader --serviceaccount=web:apiRoleBinding (--user, --group also work)
kubectl create clusterrolebinding devs-view --clusterrole=view --group=devsClusterRoleBinding
kubectl create quota web --hard=pods=50,requests.cpu=20ResourceQuota
kubectl create pdb api --selector=app=api --max-unavailable=1PodDisruptionBudget
kubectl create priorityclass high --value=1000PriorityClass

ConfigMap, Secret and token generators are in ConfigMaps and Secrets.

edit, patch and replace

CommandDoes
kubectl edit deploy/apiopen in KUBE_EDITOR or EDITOR; if the save is rejected the edited copy is kept under /tmp
KUBE_EDITOR="code --wait" kubectl edit cm api-configanother editor
kubectl patch deploy api -p '{"spec":{"replicas":3}}'strategic merge patch (the default)
kubectl patch deploy api --type=merge -p '{"spec":{"paused":false}}'JSON merge patch
kubectl patch deploy api --type=json -p '[{"op":"remove","path":"/spec/template/spec/nodeSelector"}]'JSON patch operations
kubectl patch deploy api --patch-file=patch.yamlthe patch from a file (YAML or JSON)
kubectl patch deploy api --subresource=scale --type=merge -p '{"spec":{"replicas":2}}'patch a subresource (status, scale, resize)
kubectl replace -f app.yamlsend the whole object (HTTP PUT)
kubectl replace --force -f pod.yamldelete and recreate, to change immutable fields
--typeListsUse it on
strategicmerged by key: containers by name, ports by containerPortbuilt-in kinds only
mergereplaced whole; null deletes a keyanything, including custom resources
jsonadd, remove, replace, move, copy, test by path and indexanything; precise list edits

Labels, annotations and set

CommandDoes
kubectl label pod api-7d9f tier=webadd a label
kubectl label pod api-7d9f tier=api --overwritechange one
kubectl label pod api-7d9f tier-remove one (trailing -)
kubectl label pods -l app=api canary=truemany at once; --all for every object of the type
kubectl label pod api-7d9f --listshow labels
kubectl annotate deploy api kubernetes.io/change-cause="1.4.3: fix login"annotate; shows in rollout history
kubectl annotate deploy api team-remove an annotation
kubectl set image deploy/api api=ghcr.io/acme/api:1.4.3CONTAINER=IMAGE; *=IMAGE sets every container
kubectl set env deploy/api LOG_LEVEL=debugadd or change an env var (starts a rollout)
kubectl set env deploy/api LOG_LEVEL-remove it
kubectl set env deploy/api --from=secret/api-secretsimport every key of a Secret or ConfigMap
kubectl set env deploy/api --listprint the env vars
kubectl set resources deploy/api -c api --requests=cpu=250m,memory=256Mi --limits=memory=256Mirequests and limits
kubectl set serviceaccount deploy/api apichange the ServiceAccount

Deleting

CommandDoes
kubectl delete -f app.yamlwhat the files describe
kubectl delete pod api-7d9fgraceful: SIGTERM, then SIGKILL after terminationGracePeriodSeconds (30 s default)
kubectl delete pods -l app=apiby label
kubectl delete pods -A --field-selector=status.phase=Failedby field, everywhere
kubectl delete deploy --all -n webevery Deployment in the namespace
kubectl delete ns webthe namespace and everything in it
-i, --interactiveconfirm each object first
--grace-period=60, --nowoverride the grace period; --now is --grace-period=1
--wait=falsereturn at once instead of waiting for finalizers
--cascade=orphankeep the dependents (delete a Deployment, keep its ReplicaSets and Pods)
--cascade=foregrounddelete dependents first, then the owner
--ignore-not-foundno error when it's already gone
--forceremove the object from the API at once (grace period 0)

Workloads and rollouts

CommandDoes
kubectl rollout status deploy/api --timeout=5mwait for the rollout; non-zero exit on failure or timeout
kubectl rollout history deploy/apirevisions with their CHANGE-CAUSE
kubectl rollout history deploy/api --revision=3that revision's Pod template
kubectl rollout undo deploy/apiback to the previous revision
kubectl rollout undo deploy/api --to-revision=3to a given one
kubectl rollout restart deploy/apireplace every Pod with an unchanged spec
kubectl rollout restart deploy -n webevery Deployment in the namespace; -l tier=backend for some
kubectl rollout pause deploy/api, resumebatch several edits into one rollout (Deployments only)
kubectl scale deploy/api --replicas=5set replicas
kubectl scale sts/db --replicas=3StatefulSets scale in order; PVCs are kept by default
kubectl scale deploy --all --replicas=0 -n webstop a namespace's Deployments
kubectl scale deploy/api --current-replicas=2 --replicas=4only if it is currently 2
kubectl autoscale deploy/api --min=2 --max=10 --cpu=70%create an HPA; --memory=80%; --cpu-percent is deprecated
kubectl get hpa -wwatch targets and replica counts

status, history, undo and restart work on Deployments, DaemonSets and StatefulSets. restart sets a kubectl.kubernetes.io/restartedAt annotation on the Pod template, which is also why it is the usual way to pick up changed ConfigMap or Secret values.

kubectl create deploy api --image=…/api:1.4.2 --replicas=3 kubectl set image deploy/api api=…/api:1.4.3 kubectl rollout status deploy/api kubectl rollout status deploy/api kubectl rollout undo deploy/api Deployment api Pod template: image 1.4.2 Pod template: image 1.4.3 Pod template: image 1.4.3 Pod template: image 1.4.3 Pod template: image 1.4.2 ReplicaSet api-7d9f ReplicaSet api-5c2b rev 1 · replicas 3 rev 1 · replicas 3 rev 1 · replicas 2 rev 1 · replicas 0 rev 3 · replicas 3 rev 2 · replicas 1 rev 2 · replicas 2 rev 2 · replicas 3 rev 2 · replicas 0 1.4.2 1.4.2 1.4.2 stop no Pods start 1.4.3 start 1.4.3 1.4.3 no Pods 3 Pods ready. Defaults for 3 replicas: maxSurge 25% → 1 extra, maxUnavailable 25% → 0, so never fewer than 3 ready a new template makes a new ReplicaSet; it adds 1 Pod first: 4 Pods, still 3 ready the new Pod is ready, so one old Pod stops and the next new one starts; this repeats all 3 Pods run 1.4.3. The old ReplicaSet stays at 0 so that rollout undo can scale it back up undo puts rev 1's template back: api-7d9f scales up again the same rolling way and is now revision 3

1/5

A Deployment with 3 replicas owns ReplicaSet api-7d9f (revision 1), which keeps 3 Pods of image 1.4.2 running.

Logs, exec and port-forward

kubectl logsDoes
kubectl logs api-7d9fa single-container Pod
-c api, --all-containersone container, all of them
-ffollow
-p, --previousthe previous, crashed instance
--since=10m, --since-time=2026-11-11T09:00:00Zrecent lines only
--tail=100last N lines; default all, but 10 when you use -l
--timestamps, --prefixadd timestamps, [pod/name/container]
-l app=api -f --max-log-requests=10several Pods by label; following is capped at 5 streams by default
kubectl logs deploy/apione Pod of a Deployment, Job or Service
kubectl logs deploy/api --all-podsevery Pod of it, prefixed
--limit-bytes=1048576cap the output
CommandDoes
kubectl exec -it api-7d9f -- shinteractive shell; everything after -- is the command
kubectl exec -it deploy/api -c api -- bashpick a Pod through its owner, and a container
kubectl exec api-7d9f -- envone command, no TTY
kubectl exec api-7d9f -- sh -c 'echo $HOSTNAME'shell features need an explicit sh -c
kubectl exec -i db-0 -- psql -U app < dump.sqlfeed stdin
kubectl attach -it api-7d9fattach to the main process's stdin and stdout
kubectl port-forward svc/api 8080:80localhost:8080 to Service port 80
kubectl port-forward pod/api-7d9f 9229same port on both sides
kubectl port-forward deploy/api :3000a random free local port, printed
kubectl port-forward svc/db 5432 --address 0.0.0.0listen on every interface, not just localhost
kubectl cp api-7d9f:/tmp/dump ./dumpcopy out; needs tar in the image
kubectl cp ./conf web/api-7d9f:/etc/app -c apicopy in, with namespace and container
kubectl proxy --port=8001the API on localhost:8001 with your credentials

port-forward to a Service or Deployment picks one Pod and stays on it, with no load balancing; it ends when that Pod goes away.

kubectl your laptop kube-apiserver authn, RBAC node worker-2 kubelet :10250 log files /var/log/pods/… Pod api-7d9f container api port 3000 logs reads the log exec -it starts sh port-forward listens :8080 port-forward skips Services: one Pod, no load balancing
logs, exec and port-forward all go through the API server to the kubelet, so they need only API access, not a route into the Pod network.

Debugging

kubectl debug

CommandDoes
kubectl debug -it api-7d9f --image=busybox:1.37 --target=apiephemeral container in the running Pod, sharing the api container's processes
kubectl debug -it api-7d9f --image=busybox:1.37 --copy-to=api-dbg --share-processesa copy of the Pod plus a debug container
kubectl debug -it api-7d9f --copy-to=api-dbg --container=api -- sha copy with the app's command replaced by a shell
kubectl debug api-7d9f --copy-to=api-dbg --set-image=api=ghcr.io/acme/api:debuga copy with other images
kubectl debug node/worker-2 -it --image=busybox:1.37a Pod in the node's namespaces; node filesystem at /host (chroot /host)
--profile=sysadminprivileged; others: general (default), baseline, restricted, netadmin
--custom=debug.yamla partial container spec merged into the debug container (env, mounts, security context)

Ephemeral containers can't be removed: they stay in the Pod until it is deleted. Node debug Pods (node-debugger-worker-2-…) and --copy-to copies stay until you delete them.

Events and usage

CommandDoes
kubectl events -n webnamespace events, oldest first
kubectl events --for pod/api-7d9fone object
kubectl events --for deploy/api -wthen keep watching
kubectl events -A --types=Warningonly warnings, everywhere
kubectl get events --sort-by=.metadata.creationTimestampthe older way; also .lastTimestamp
kubectl get events --field-selector involvedObject.name=api-7d9fby object, server-side
kubectl top pods -A --sort-by=memorylive usage; --sort-by=cpu
kubectl top pod api-7d9f --containersper container
kubectl top pods --sumadds a total row
kubectl top nodesnode usage; --show-capacity uses capacity instead of allocatable
kubectl get --raw /api/v1/nodes/worker-2/proxy/stats/summarythe kubelet's stats through the API server

Events are kept for about an hour by default. top needs metrics-server and shows a short recent average, not a peak.

See the status-by-status flowchart in Kubernetes debugging for what to look at once you have the output.

Nodes

CommandDoes
kubectl get nodes -o wideversions, internal IPs, OS, container runtime
kubectl get nodes -L topology.kubernetes.io/zone,node.kubernetes.io/instance-typelabels as columns
kubectl describe node worker-2conditions, taints, capacity, allocated requests and limits
kubectl get pods -A -o wide --field-selector spec.nodeName=worker-2what runs there
kubectl cordon worker-2mark unschedulable; running Pods stay
kubectl drain worker-2 --ignore-daemonsets --delete-emptydir-datacordon, then evict every Pod
kubectl uncordon worker-2schedulable again
kubectl cordon -l nodepool=oldmany nodes by label
kubectl taint nodes worker-2 dedicated=gpu:NoScheduleadd a taint
kubectl taint nodes worker-2 dedicated=gpu:NoSchedule-remove that taint
kubectl taint nodes worker-2 dedicated-remove every taint with that key
kubectl label node worker-2 disktype=ssdlabel for nodeSelector and affinity
kubectl label node worker-2 node-role.kubernetes.io/worker=fills the ROLES column
drain flagDoes
--ignore-daemonsetsneeded whenever DaemonSet Pods exist; they stay on the node
--delete-emptydir-dataallow evicting Pods with emptyDir volumes (their data is lost)
--forcealso delete Pods without a controller; nothing recreates them
--grace-period=60override each Pod's grace period
--timeout=10mgive up after this long; the default waits forever
--pod-selector='app!=batch'evict only matching Pods
--disable-evictiondelete instead of evict, which bypasses PodDisruptionBudgets
--skip-wait-for-delete-timeout=60don't wait for Pods that have been terminating longer than this
--dry-run=clientlist what would be evicted

drain uses the Eviction API, which honors PodDisruptionBudgets: if a PDB allows no disruption right now, drain keeps retrying until --timeout.

Access and RBAC

CommandDoes
kubectl auth whoamithe user name and groups the API server sees for you
kubectl auth can-i create deploy -n webyes or no; -q for the exit code only
kubectl auth can-i '*' '*' -Aare you effectively cluster-admin?
kubectl auth can-i --list -n webeverything you may do in a namespace
kubectl auth can-i get pods --as=jane --as-group=devscheck for someone else
kubectl auth can-i list secrets -n web --as=system:serviceaccount:web:apicheck a ServiceAccount
kubectl get pods -n web --as=system:serviceaccount:web:apirun any command as that identity
kubectl create token api -n web --duration=1hshort-lived ServiceAccount token
kubectl auth reconcile -f rbac.yamlcreate or extend RBAC objects; --remove-extra-permissions to also trim
kubectl get rolebindings,clusterrolebindings -A -o widewho is bound to which role
kubectl describe clusterrole viewthe rules of a role

The who-can plugin answers the reverse question: kubectl who-can delete pods -n web.

ConfigMaps and Secrets

CommandDoes
kubectl create configmap api-config --from-literal=LOG_LEVEL=infofrom key=value pairs
kubectl create configmap api-config --from-file=app.tomlkey is the file name
kubectl create configmap api-config --from-file=config=app.tomlkey config
kubectl create configmap api-config --from-file=conf/one key per file in a directory
kubectl create configmap api-config --from-env-file=.envone key per KEY=value line
kubectl create secret generic db --from-file=password=./pw.txtthe same flags, stored base64-encoded
kubectl create secret tls api-tls --cert=tls.crt --key=tls.keykubernetes.io/tls
kubectl create secret docker-registry ghcr --docker-server=ghcr.io --docker-username=acme --docker-password="$TOKEN"image pull secret
kubectl get secret db -o jsonpath='{.data.password}' | base64 -ddecode one key
kubectl get secret api-tls -o jsonpath='{.data.tls\.crt}' | base64 -da key with a dot
kubectl get secret db -o json | jq '.data | map_values(@base64d)'decode every key
kubectl describe secret dbkey names and sizes, no values
kubectl get cm api-config -o jsonpath='{.data.app\.toml}'one ConfigMap key

create fails if the object exists. To create or update in one step, generate and apply:

kubectl create configmap api-config --from-file=app.toml \
  --dry-run=client -o yaml | kubectl apply -f -
# env vars never reload: restart the Pods
kubectl rollout restart deploy/api

Waiting

kubectl wait blocks until a condition holds; it waits 30 s by default (--timeout; 0 checks once).

CommandWaits until
kubectl wait --for=condition=Available deploy/api --timeout=5mthe Deployment is available
kubectl wait --for=condition=Ready pod -l app=apievery matching Pod is ready
kubectl wait --for=condition=Complete job/migrate --timeout=10mthe Job succeeded (condition=Failed for the opposite)
kubectl wait --for=jsonpath='{.status.phase}'=Running pod/api-7d9fa field has a value
kubectl wait --for=jsonpath='{.status.loadBalancer.ingress}' svc/weba field exists
kubectl wait --for=create secret/api-tls --timeout=60sthe object exists
kubectl wait --for=delete pod/api-7d9f --timeout=60sit is gone
kubectl wait --for=condition=Established crd/certificates.cert-manager.ioa CRD is usable

Plugins and krew

A plugin is any executable named kubectl-name on your PATH; kubectl name runs it (kubectl-foo-bar becomes kubectl foo bar). kubectl plugin list shows what it finds. krew is the plugin manager: install it with brew install krew or the script on the krew site, then add ${KREW_ROOT:-$HOME/.krew}/bin to your PATH.

CommandDoes
kubectl krew updaterefresh the plugin index
kubectl krew search logfind plugins
kubectl krew install ctx ns neatinstall
kubectl krew list, info treeinstalled plugins, details of one
kubectl krew upgradeupgrade all
kubectl krew uninstall neatremove
PluginDoesExample
ctx, nskubectx and kubenskubectl ns web
neatstrips status, managed fields and defaults from YAMLkubectl get deploy api -o yaml | kubectl neat
treeownership tree (Deployment, ReplicaSets, Pods)kubectl tree deploy api
sterntail many Pods at once, coloredkubectl stern api -n web
view-secretdecode a Secretkubectl view-secret db password
who-canwhich subjects hold a permissionkubectl who-can get secrets -n web
resource-capacityrequests, limits and usage per nodekubectl resource-capacity --util
node-shellroot shell on a nodekubectl node-shell worker-2
df-pvdisk usage of PersistentVolumeskubectl df-pv
get-allevery object, beyond get allkubectl get-all --namespace=web

Outside kubectl, k9s is a terminal UI for the same tasks (k9s -n web).

Shell setup

# needs the bash-completion package
echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc
~/.bashrc or ~/.zshrc
alias k=kubectl
alias kg='kubectl get'
alias kd='kubectl describe'
alias kaf='kubectl apply -f'
alias kl='kubectl logs -f'
alias kx='kubectl exec -it'
alias kns='kubectl config set-context --current --namespace'
# scaffold YAML: k create deploy api --image=x $dry
export dry='--dry-run=client -o yaml'

kuberc (~/.kube/kuberc, or the file in KUBERC; KUBERC=off disables it) holds kubectl preferences separate from cluster credentials: default flags per command and your own aliases. Flags you type still win.

~/.kube/kuberc
apiVersion: kubectl.config.k8s.io/v1beta1
kind: Preference
defaults:
  - command: delete
    options:
      - name: interactive      # always confirm deletes
        default: "true"
  - command: apply
    options:
      - name: server-side
        default: "true"
aliases:                  # getn = get nodes -o wide
  - name: getn
    command: get
    prependArgs: [nodes]
    options:
      - name: output
        default: wide

kubectl kuberc view prints it; kubectl kuberc set --section aliases --name getn --command get --prependarg nodes --option output=wide writes the same alias for you.

Quick lookup

I want to…Command
switch clusterkubectl config use-context prod or kubectx prod
switch default namespacekubectl config set-context --current --namespace=web or kubens web
see which cluster I'm onkubectl config current-context
list everything runningkubectl get pods -A -o wide
find a Pod by labelkubectl get pods -l app=api -o wide
see why a Pod is Pendingkubectl describe pod P (Events at the bottom)
see why a Pod crashedkubectl logs P --previous
follow logs of a Deploymentkubectl logs deploy/api -f --all-pods
get a shellkubectl exec -it P -- sh
debug a distroless Podkubectl debug -it P --image=busybox:1.37 --target=CONTAINER
open a Service locallykubectl port-forward svc/api 8080:80
preview a changekubectl diff -f k8s/
apply a directorykubectl apply -f k8s/ -R
scaffold a manifestkubectl create deploy api --image=x --dry-run=client -o yaml
change an imagekubectl set image deploy/api api=ghcr.io/acme/api:1.4.3
restart Podskubectl rollout restart deploy/api
undo a bad rolloutkubectl rollout undo deploy/api
scalekubectl scale deploy/api --replicas=5
run a CronJob nowkubectl create job manual-1 --from=cronjob/report
decode a Secretkubectl get secret db -o jsonpath='{.data.password}' | base64 -d
copy a file outkubectl cp P:/path/file ./file
see recent warningskubectl events -A --types=Warning
see CPU and memorykubectl top pods -A --sort-by=cpu
check what I may dokubectl auth can-i --list -n web
check a ServiceAccount's rightskubectl auth can-i get secrets --as=system:serviceaccount:web:api
find which API group a kind is inkubectl api-resources | grep -i gateway
read a field's docskubectl explain deploy.spec.strategy
get one fieldkubectl get pod P -o jsonpath='{.status.podIP}'
sort by agekubectl get pods --sort-by=.metadata.creationTimestamp
watch changeskubectl get pods -w
wait in a scriptkubectl wait --for=condition=Available deploy/api --timeout=5m
take a node outkubectl drain N --ignore-daemonsets --delete-emptydir-data
put it backkubectl uncordon N
remove a taintkubectl taint nodes N key:NoSchedule-
delete stuck failed Podskubectl delete pods -A --field-selector=status.phase=Failed
force-delete a Pod on a dead nodekubectl delete pod P --force
see the raw API callsadd -v=6 to any command
export an object for reusekubectl get deploy api -o yaml | kubectl neat > api.yaml

References