| Tested on | Rocky Linux 10.2 (Red Quartz) workstation |
|---|---|
| Package | kubectl 1.36.3 |
| Applies to | Ubuntu, Debian, Kali Linux, Linux Mint, Pop!_OS, Raspberry Pi OS, elementary OS, Zorin OS, Parrot OS, MX Linux, RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora Kubernetes cluster |
| Cert prep | CKA · CKAD |
| Lab environment | Any Kubernetes cluster — install Kubernetes with kubeadm for the lab used here |
| Privilege | Normal user (no sudo required on the workstation) |
| Man page | kubectl command reference |
| Scope | Imperative manifest generation, client and server dry-run, kubectl apply and patch, kubectl explain, JSONPath, custom columns, diff, scale, and label workflows. Does not cover cluster installation, RBAC design, or Helm/Kustomize project layout. |
| Related guides | Kubernetes annotations |
kubectl — quick reference
Get, describe, and output formats
List objects and shape how kubectl prints them.
| When to use | Command |
|---|---|
| List resources in the current or named namespace | kubectl get pods / kubectl get deploy -n NAMESPACE |
| Show extra columns (node, IP, image) | kubectl get pods -o wide |
| Return one field with JSONPath | kubectl get pods -o jsonpath='{.items[*].metadata.name}' |
| Build your own table from object fields | kubectl get pods -o custom-columns=NAME:.metadata.name,IP:.status.podIP |
| Print full API representation | kubectl get deploy NAME -o yaml / -o json |
| Show events and conditions for one object | kubectl describe pod NAME |
| Filter by label selector | kubectl get pods -l app=web,tier=frontend |
Imperative create and generate YAML
Fast exam-style object creation; pair with --dry-run=client -o yaml to capture manifests.
| When to use | Command |
|---|---|
| Create a Deployment from an image | kubectl create deployment NAME --image=IMAGE --replicas=N |
| Create a ConfigMap from literals | kubectl create configmap NAME --from-literal=key=value |
| Create a generic Secret from literals | kubectl create secret generic NAME --from-literal=key=value |
| Expose a workload as a ClusterIP Service | kubectl expose deployment NAME --port=80 --target-port=80 |
| Run a one-off Pod (debug / throwaway) | kubectl run NAME --image=IMAGE --restart=Never |
| Print starter YAML without persisting the object | kubectl create deployment NAME --image=IMAGE --dry-run=client -o yaml |
Declarative apply, diff, and replace
Manage manifests stored in Git or on disk.
| When to use | Command |
|---|---|
| Create or update objects from a file or stdin | kubectl apply -f manifest.yaml |
| Validate against the API server without persisting | kubectl apply -f manifest.yaml --dry-run=server |
| Show what would change before apply | kubectl diff -f manifest.yaml |
| Replace an object wholesale (overwrite) | kubectl replace -f manifest.yaml |
| Apply a directory of manifests | kubectl apply -f ./manifests/ |
| Delete objects defined in a manifest | kubectl delete -f manifest.yaml |
Patch, scale, and metadata
Change live objects without re-applying the full file.
| When to use | Command |
|---|---|
| Strategic merge patch (default for supported built-in resource types; not supported for CRDs) | kubectl patch deploy NAME -p '{"spec":{"replicas":3}}' |
| JSON Patch (RFC 6902 operations) | kubectl patch deploy NAME --type=json -p='[{"op":"replace","path":"/spec/replicas","value":3}]' |
| Merge patch (object fields merge; lists in the patch replace the whole list) | kubectl patch deploy NAME --type=merge -p '{"spec":{"replicas":3}}' |
| Scale a controller | kubectl scale deployment NAME --replicas=N |
| Change a container image | kubectl set image deployment/NAME CONTAINER=IMAGE:TAG |
| Add or change labels | kubectl label deploy NAME tier=web --overwrite |
| Add or change annotations | kubectl annotate deploy NAME owner=team-a --overwrite |
Schema discovery and dry-run modes
| When to use | Command |
|---|---|
| Document a resource field while writing YAML | kubectl explain pod.spec.containers |
| Drill into nested fields | kubectl explain deployment.spec.strategy --recursive |
| Preview the object without submitting the write request | kubectl apply -f file.yaml --dry-run=client |
| API server admission without persistence | kubectl apply -f file.yaml --dry-run=server |
| List API resources on the connected cluster | kubectl api-resources |
| Print supported API versions | kubectl api-versions |
Context, namespace, and global flags
| When to use | Command |
|---|---|
| Pin a namespace for one command | kubectl get pods -n kube-system |
| Set default namespace in kubeconfig | kubectl config set-context --current --namespace=dev |
| Target another cluster context | kubectl --context=CONTEXT get nodes |
| Impersonate a user or ServiceAccount | kubectl --as=system:serviceaccount:ns:sa get pods |
| Confirm RBAC before a change | kubectl auth can-i create deployments -n NAMESPACE |
kubectl — command syntax
kubectl sends HTTPS requests to the Kubernetes API server using credentials from kubeconfig. Synopsis from kubectl --help on kubectl 1.36.3:
kubectl controls the Kubernetes cluster manager.
Basic Commands (Beginner):
create Create a resource from a file or from stdin
expose Take a replication controller, service, deployment or pod and expose it as a new Kubernetes service
run Run a particular image on the cluster
set Set specific features on objects
Basic Commands (Intermediate):
explain Get documentation for a resource
get Display one or many resources
edit Edit a resource on the server
delete Delete resources by file names, stdin, resources and names, or by resources and label selector
Deploy Commands:
rollout Manage the rollout of a resource
scale Set a new size for a deployment, replica set, or replication controller
autoscale Auto-scale a deployment, replica set, stateful set, or replication controller
Advanced Commands:
diff Diff the live version against a would-be applied version
apply Apply a configuration to a resource by file name or stdin
patch Update fields of a resource
replace Replace a resource by file name or stdin
wait Wait for a specific condition on one or many resources
kustomize Build a kustomization target from a directory or URLInstall kubectl and point it at a cluster before you run the examples — see install kubectl and configure kubeconfig. If commands hang against a private API address, check whether an HTTP proxy is intercepting traffic; extend NO_PROXY for cluster networks or unset proxy variables in that shell.
kubectl — command examples
Essential Generate YAML with client dry-run
Use client dry-run when you want a starter manifest that is not submitted for persistence — common in CKAD/CKA tasks where you paste YAML into an editor.
Run the command:
kubectl create deployment web --image=nginx:1.27 --replicas=2 --dry-run=client -o yamlSample output:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web
strategy: {}
template:
metadata:
labels:
app: web
spec:
containers:
- image: nginx:1.27
name: nginx
resources: {}
status: {}Save the YAML, edit fields such as resources or probes, then apply it with kubectl apply -f. Drop the status: {} line from manifests you write — Kubernetes controllers and system components populate status after the object exists; kubectl does not fill it in for you.
Essential Validate with server dry-run
Server dry-run sends the object through admission controllers and validation webhooks. Use it when client-side YAML looks fine but the API might reject it.
Run the command:
kubectl apply -f - --dry-run=server <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: demo-cm
namespace: demo
data:
key: value
EOFSample output:
configmap/demo-cm created (server dry run)The (server dry run) suffix confirms nothing was persisted. Create the demo namespace first with kubectl create namespace demo if it does not exist.
Essential Inspect fields with kubectl explain
kubectl explain reads OpenAPI schema from the API server — faster than guessing field names when you write YAML. For deeper discovery of groups and versions, see Kubernetes API resources.
Run the command:
kubectl explain pod.spec.containers --api-version=v1 | head -12Sample output:
KIND: Pod
VERSION: v1
FIELD: containers <[]Container>
DESCRIPTION:
List of containers belonging to the pod. Containers cannot currently be
added or removed. There must be at least one container in a Pod. Cannot be
updated.Add --recursive on supported types to print the whole subtree, for example kubectl explain deployment.spec --recursive.
Essential Filter output with JSONPath
JSONPath pulls one or more fields from kubectl get -o json without dumping the full object — useful in scripts and quick cluster audits.
Run the command:
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.kubeletVersion}{"\n"}{end}'Sample output:
k8s-cp v1.36.3
worker01 v1.36.3{range .items[*]} iterates through every node object, while {"\t"} and {"\n"} format the output into columns and rows. Use {.items[0].metadata.name} when you need a single match.
Essential Apply a Deployment from YAML
Declarative apply is the default workflow for manifests you store in Git. kubectl creates the object on first apply and patches it on later runs.
Create a lab namespace if you need one:
kubectl create namespace demoCreate the manifest and apply it:
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
namespace: demo
labels:
app: nginx-demo
spec:
replicas: 2
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
EOFSample output:
deployment.apps/nginx-demo createdWait until replicas are available:
kubectl rollout status deployment/nginx-demo -n demo --timeout=60sSample output:
deployment "nginx-demo" successfully rolled outcreated on the first apply becomes configured when you change the same manifest and apply again.
Common Build tables with custom columns
Custom columns are readable in the terminal and map cleanly to the fields you care about during rollouts.
Run the command:
kubectl get pods -n demo -l app=nginx-demo -o custom-columns=NAME:.metadata.name,IP:.status.podIP,NODE:.spec.nodeName,READY:.status.containerStatuses[0].readySample output:
NAME IP NODE READY
nginx-demo-846cc57b79-fv6cb 192.168.5.6 worker01 true
nginx-demo-846cc57b79-x79xp 192.168.5.5 worker01 truePods still starting may show <none> for IP and false for READY until kubelet reports the container state.
Common Patch a live Deployment
Patch when you need a one-field change and do not want to open the full YAML — exams often test strategic merge and JSON Patch syntax.
Run the strategic merge patch (default):
kubectl patch deployment nginx-demo -n demo -p '{"spec":{"replicas":3}}'Sample output:
deployment.apps/nginx-demo patchedConfirm the new replica count:
kubectl get deployment nginx-demo -n demo -o jsonpath='{.spec.replicas}{"\n"}'Sample output:
3For explicit JSON Patch operations, add --type=json and a list of op, path, and value entries.
Common Scale, relabel, and retag imperatively
Imperative commands mutate objects in place. They are quick for labs; prefer YAML in production so changes stay reviewable.
Scale the Deployment:
kubectl scale deployment nginx-demo -n demo --replicas=2Sample output:
deployment.apps/nginx-demo scaledAdd a label and change the container image:
kubectl label deployment nginx-demo -n demo tier=web --overwritekubectl set image deployment/nginx-demo -n demo nginx=nginx:1.27-alpineSample output:
deployment.apps/nginx-demo labeled
deployment.apps/nginx-demo image updatedVerify labels on the controller:
kubectl get deployment nginx-demo -n demo --show-labelsSample output:
NAME READY UP-TO-DATE AVAILABLE AGE LABELS
nginx-demo 2/2 2 2 54s app=nginx-demo,tier=webset image triggers a rolling update when the image reference changes.
Common Preview changes with kubectl diff
kubectl diff compares the live object with the version that would result if the supplied manifest were applied. Exit code 1 with a unified diff is normal when changes exist.
Run the command:
kubectl diff -f /tmp/nginx-demo-diff.yamlSample output:
diff -u -N /tmp/LIVE-784548609/apps.v1.Deployment.demo.nginx-demo /tmp/MERGED-1271217826/apps.v1.Deployment.demo.nginx-demo
--- /tmp/LIVE-784548609/apps.v1.Deployment.demo.nginx-demo
+++ /tmp/MERGED-1271217826/apps.v1.Deployment.demo.nginx-demo
@@ -17,7 +17,7 @@
spec:
progressDeadlineSeconds: 600
- replicas: 2
+ replicas: 5
revisionHistoryLimit: 10The minus and plus lines show what apply would change. Generate /tmp/nginx-demo-diff.yaml from your edited manifest or export the live object, change one field, and diff again.
Advanced Expose a Deployment as a Service
Imperative expose creates a Service that selects Pod labels from an existing controller — handy when you already have Pods running and need a ClusterIP quickly.
Run the command:
kubectl expose deployment nginx-demo -n demo --port=80 --target-port=80 --name=nginx-demoSample output:
service/nginx-demo exposedList the Service:
kubectl get svc nginx-demo -n demoSample output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-demo ClusterIP 10.100.130.182 <none> 80/TCP 5sFor production, check the generated Service into Git or capture a manifest with client dry-run. kubectl expose reads and reuses the Deployment selector:
kubectl expose deployment nginx-demo -n demo --port=80 --target-port=80 --name=nginx-demo --dry-run=client -o yamlAdvanced Create a ConfigMap and capture YAML
Literal ConfigMaps are common for exam questions. Client dry-run prints the manifest you can paste into an editor.
Run the command:
kubectl create configmap demo-cm2 -n demo --from-literal=env=prod --dry-run=client -o yamlSample output:
apiVersion: v1
data:
env: prod
kind: ConfigMap
metadata:
name: demo-cm2
namespace: demoMount or reference this ConfigMap from a Pod env, envFrom, or volume — see the ConfigMap tutorial when you wire it into a workload.
kubectl — when to use / when not
| Use kubectl when | Use something else when |
|---|---|
|
|
Imperative kubectl vs declarative apply
Imperative (create, run, expose, set) |
Declarative (apply, Git-managed YAML) |
|
|---|---|---|
| Source of truth | Last command wins; harder to audit | Files in version control |
| Idempotency | create fails if the object exists |
apply updates the same object safely |
| Exam speed | Fast for one-off objects | Fast when you already have a manifest |
| Rollback story | kubectl rollout undo on supported controllers (works after imperative or declarative changes) |
Re-apply a known-good manifest from Git, or kubectl rollout undo to revert the live controller |
| Generation tip | Add --dry-run=client -o yaml |
Add --dry-run=server before first real apply |
Most teams use declarative apply in CI/CD and imperative commands for debugging. CKAD rewards knowing both.
kubectl — interview corner
What is the difference between --dry-run=client and --dry-run=server?
Client dry-run prints the object kubectl would send without submitting it. API server validation and admission do not run.
Server dry-run sends the request with a dry-run flag. The API server runs validation and admission, then discards the change. You see errors such as forbidden fields or quota violations before anything is stored.
A strong answer is:
"Client dry-run prints the object without sending the write request. Server dry-run asks the API server to validate and admit without storing — I use server dry-run before applying risky manifests."
When do you use JSONPath versus custom-columns?
Both read fields from API objects. JSONPath fits scripts and one-line extractions (-o jsonpath=...). custom-columns prints a stable human table with named headers.
JSONPath can walk arrays with {range}; custom-columns use dotted paths and display <none> for missing values.
A strong answer is:
"JSONPath for automation and single values; custom-columns when I want a readable table in the shell. Both beat grepping raw YAML."
What patch types does kubectl patch support?
Common --type values:
- strategic (default for supported built-in resource types; not supported for CRDs) — merge lists with merge keys
- merge — JSON merge patch (RFC 7386); object fields merge, but any list you include replaces the whole list
- json — JSON Patch (RFC 6902) with
op,path,value
Wrong patch type can replace a whole list instead of updating one entry.
A strong answer is:
"Strategic merge is the default for supported built-in resources such as Deployments and Pods; JSON Patch is useful for explicit operations on individual paths."
Why prefer kubectl apply over kubectl create for production?
kubectl create fails when the object already exists. kubectl apply records last-applied configuration and performs a three-way merge on updates, so the same file can be run repeatedly from CI.
create is still useful with --dry-run=client -o yaml to generate a first manifest.
A strong answer is:
"create is one-shot; apply is idempotent and merge-aware — production manifests should be applied from Git, not recreated."
How does kubectl explain help you write YAML?
kubectl explain queries the OpenAPI schema published by the API server. Start at the resource (pod.spec) and drill into nested fields (pod.spec.containers.resources).
--recursive prints nested fields in one pass for large types such as Pod or Deployment.
A strong answer is:
"explain shows field types, defaults, and descriptions from the live API — I use it instead of guessing YAML keys or copying outdated blog posts."
Why does kubectl diff exit with code 1 when a diff exists?
kubectl diff follows the Unix diff convention: exit code 0 means no changes, 1 means differences were printed, and higher codes mean failure.
In scripts, treat 1 as "changes detected", not necessarily an error.
A strong answer is:
"Exit 1 only means a diff was found — I still read the +/- lines, then run apply if the change is intended."
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
error: the server doesn't have a resource type |
Wrong API version, typo, or CRD not installed | kubectl api-resources | grep -i '<resource-name-or-kind>' (for example kubectl api-resources | grep -i ingress); fix apiVersion in YAML |
Forbidden on apply or patch |
RBAC denies the verb on that resource | kubectl auth can-i create deployments -n NAMESPACE; fix RoleBinding |
error validating data: unknown field |
Field removed or renamed in current API | kubectl explain RESOURCE; compare apiVersion with kubectl api-resources |
AlreadyExists on create |
Object name taken in the namespace | kubectl get the object; switch to apply or pick a new name |
| Empty JSONPath output | Path does not exist on that object | kubectl get OBJECT -o json and trace the path; watch for unset status fields on new Pods |
diff shows only metadata churn |
Exported manifest includes resourceVersion, status, or last-applied annotation noise |
Strip status, metadata.resourceVersion, and managed fields before diffing |
What's Next
- Control Plane and Worker Node Components
- Create, Switch, and Manage with kubectl
- Kubernetes Labels and Selectors with Examples
References
- kubectl command reference — upstream subcommands and flags
- kubectl cheat sheet — official quick reference
- Declarative management with kubectl apply — three-way merge and last-applied configuration
- JSONPath support in kubectl — template syntax for
-o jsonpath

