Kubernetes — dry-run=client vs — dry-run=server: The Difference Every CKA/CKS Candidate Must Know
Abstract
Kubernetes — dry-run=client vs — dry-run=server: The Difference Every CKA/CKS Candidate Must Know
Abstract
One of the most overlooked Kubernetes concepts during CKA and CKS preparation is the difference between --dry-run=client and --dry-run=server. Although both commands appear similar, they serve completely different purposes.
Understanding how Kubernetes processes these requests can save valuable exam time and help troubleshoot issues related to RBAC, Pod Security Admission (PSA), admission webhooks, CRDs, and policy engines like Kyverno and Gatekeeper.
This article explains what happens behind the scenes, when to use each option, and why --dry-run=server is one of the most useful debugging tools for Kubernetes administrators.
Understanding Dry Run in Kubernetes
A dry run allows you to validate a Kubernetes operation without permanently creating or modifying resources. Kubernetes provides two different types of dry runs:
- Client-side dry run
- Server-side dry run
Although their outputs often look similar, the processing path is completely different.
Client Dry Run (--dry-run=client)
When using a client dry run, everything happens entirely inside the kubectl binary on your local machine.
This means:
- No API server communication
- No authentication or authorization (RBAC)
- No admission controllers or policy enforcement
- No schema validation by the cluster
- No object persistence
Its primary purpose is to generate manifests and verify that your command syntax is correct. Even if your API server is completely offline or you aren’t connected to a cluster, this command still works because kubectl Performs all processing locally.
Think of client dry run as asking: “Can kubectl construct this resource?”
Server Dry Run (--dry-run=server)
Server dry run behaves very differently. The request is sent to the Kubernetes API server exactly as if you intended to create the resource. However, before the object is persisted, Kubernetes discards the request after completing all validations.
During this process, Kubernetes performs:
- Authentication & Authorization (RBAC)
- Admission Controllers & Webhooks (Validating/Mutating)
- Pod Security Admission (PSA)
- OpenAPI Schema Validation & CRD Validation
- Defaulting (populating default fields)
The only difference is that the final object is never stored in etcd.
Think of server dry run as asking: “Will my Kubernetes cluster actually accept this resource?”
A Practical Example
Let’s assume you want to enforce the Restricted Pod Security Standard on a namespace named testadminplug.
First, apply the Pod Security label to the live cluster:
Bash
kubectl label --overwrite ns testadminplug pod-security.kubernetes.io/enforce=restricted
Output:
Plaintext
namespace/testadminplug labeled
Now, let’s look at how client and server dry runs handle a command attempting to spin up a privileged container inside this restricted namespace. A privileged container explicitly violates the Restricted Pod Security Standard.
Client Dry Run Execution
Run the client-side validation using kubectl create:
Bash
cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: privileged-pod
namespace:testadminplug
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: true
kubectl create -f pod.yaml --dry-run=client
Output:
YAML
pod/privileged-pod created (dry run)
Why did it succeed?
Because a client-side dry run never contacts the Kubernetes API server. kubectl simply parses your command flags and generates the structural YAML locally. Even if the namespace testadminplug didn't exist at all in the cluster, kubectl create ... --dry-run=client would still succeed. It has absolutely no knowledge of:
- Target namespace existence or its Pod Security labels
- Live RBAC permissions
- Active admission controllers or cluster policies
It only confirms that your command syntax is correct and that kubectl is capable of generating the blueprint.
Server Dry Run Execution
Now run the exact same command, but target the live cluster using the server-side validation flag:
Bash
kubectl create -f pod.yaml --dry-run=server
(Note: To perfectly trigger the security policy, let’s assume our deployment template passes a privileged security context to the container.
Output:
Error from server (Forbidden): error when creating "pod.yaml": pods "privileged-pod" is forbidden: violates PodSecurity "restricted:latest": privileged (container "nginx" must not set securityContext.privileged=true), allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
This demonstrates the core architectural difference. With a server-side dry run, the request travels to the API server. Before deciding whether to save it, the API server executes its entire validation pipeline (RBAC, Admission Controllers, and PSA).
Since the target namespace enforces the Restricted Pod Security Standard, the API server catches the violation and explicitly rejects it. However, because it was a dry run, nothing was modified in etcd.
When Should You Use Each?

Behind the Scenes
Client Dry Run Lifecycle
Plaintext
kubectl command ──► Local Syntax Validation ──► YAML/JSON Output Generated
(API Server bypassed entirely)
Server Dry Run Lifecycle
Plaintext
kubectl command ──► API Server
│
├── Authentication & Authorization (RBAC)
├── Mutating Webhooks & Defaulting
├── OpenAPI Schema Validation
├── Validating Webhooks (PSA, Kyverno, etc.)
│
└── Resource Discarded (Never stored in etcd)
Why This Matters for CKA & CKS Exams
Many exam tasks involve cluster-level security mechanisms that exist purely inside the API server. A client-side dry run cannot validate any of these because it acts purely as a local code generator.
Additionally, combining --dry-run=server with -o yaml yields an incredible troubleshooting benefit: it returns the object after mutating webhooks and defaulting have run. If you want to see exactly how the cluster modifies your resource (e.g., injecting sidecars, default ServiceAccount tokens, or storage defaults) before actually deploying it, server dry run will show you.
Whenever you need to verify whether a resource will actually be accepted and tolerated by a live Kubernetes cluster, server dry run is the safest, smartest tool at your disposal.
메타데이터
- post_id
- db76f2a058a1
- slug
- kubernetes-dry-run-client-vs-dry-run-server-the-difference-every-cka-cks-candidate-must-know-db76f2a058a1
- url
- https://medium.com/@mailtosadhu/kubernetes-dry-run-client-vs-dry-run-server-the-difference-every-cka-cks-candidate-must-know-db76f2a058a1
- canonical_url
- https://medium.com/@mailtosadhu/kubernetes-dry-run-client-vs-dry-run-server-the-difference-every-cka-cks-candidate-must-know-db76f2a058a1
- author_url
- https://medium.com/@mailtosadhu
- status
- ok
- fetched_at
- 2026-08-27 15:22:43