Ask a room of CKA candidates where a Deployment’s Pods come from and everyone answers instantly: the scheduler picks a node, the API server records it, the kubelet runs it. Ask where the kube-apiserver Pod itself comes from — the one the scheduler and API server depend on — and the room goes quiet. The answer is static Pods, and it’s one of the most reliably tested corners of the CKA exam precisely because it inverts the mental model most people build first.
A static Pod is a Pod the kubelet runs directly from a file on disk, with no controller, no scheduler, and no API server involved in the decision. Understanding them is not academic trivia: every kubeadm control plane runs as static Pods, and a classic exam task is “the API server is down, fix it” — which almost always means editing a static Pod manifest. This guide covers what static Pods are, how the kubelet finds them, mirror Pods, how the control plane uses them, and the exact commands to edit and troubleshoot them. If you’re still assembling fundamentals, start with the CKA Exam Guide 2026 and the kubeadm cluster installation guide, then come back here.
What Is a Static Pod?
A static Pod is managed directly by the kubelet on a specific node, without going through the Kubernetes control plane. The kubelet watches a directory on the node’s filesystem; when it sees a Pod manifest there, it starts that Pod, and when the file is removed, it stops the Pod.
The defining characteristics:
- No API server required. The kubelet reads the manifest from local disk and acts on it. Static Pods run even if the API server is completely down — which is exactly why the control plane itself uses them.
- The kubelet is the only controller. There is no ReplicaSet, Deployment, or scheduler behind a static Pod. The kubelet restarts the container if it crashes (subject to
restartPolicy), but nothing reschedules it to another node. - Node-bound. A static Pod lives and dies on one node. It cannot be moved. Delete the file, and the Pod is gone from that node.
- Named after the node. The running Pod’s name is automatically suffixed with the node’s hostname, e.g.
kube-apiserver-controlplane.
Contrast this with an ordinary Pod, which is an API object the scheduler assigns and the kubelet then runs on the chosen node. With a static Pod, the flow is reversed: the file is the source of truth, and the API server only ever sees a read-only reflection of it.
The Manifest Path: Where the kubelet Looks
The kubelet watches a directory defined by the staticPodPath setting in its configuration. On a standard kubeadm cluster this is:
/etc/kubernetes/manifests
You can confirm the path in two ways. First, look at the kubelet config file that kubeadm writes:
grep staticPodPath /var/lib/kubelet/config.yaml
# staticPodPath: /etc/kubernetes/manifests
Second, inspect the running kubelet to find which config file it’s using:
# See the --config flag passed to the kubelet
systemctl cat kubelet | grep -i config
ps -ef | grep kubelet | grep -o '\--config=[^ ]*'
The kubelet config (/var/lib/kubelet/config.yaml) is the modern source of staticPodPath. In much older setups you might instead see a --pod-manifest-path command-line flag on the kubelet — the exam skews toward the config-file form, but recognize both.
Drop any valid Pod manifest into that directory and the kubelet starts it within seconds. No kubectl apply, no scheduling decision. Remove the file and the Pod terminates.
# Create a static Pod by writing a manifest to the path
cat <<'EOF' > /etc/kubernetes/manifests/static-web.yaml
apiVersion: v1
kind: Pod
metadata:
name: static-web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
EOF
# The kubelet picks it up automatically — no apply needed
Mirror Pods: How Static Pods Show Up in kubectl
Here’s the detail the exam loves. Although a static Pod is managed entirely by the kubelet, the kubelet creates a read-only copy of it in the API server so you can see it with kubectl get pods. That copy is called a mirror Pod.
Key facts about mirror Pods:
- The mirror Pod’s name is the manifest’s
metadata.nameplus a hyphen and the node name:static-webbecomesstatic-web-controlplane. - You cannot control the static Pod through its mirror. Running
kubectl delete pod static-web-controlplanedeletes the mirror, but the kubelet immediately recreates it because the file on disk still exists. To truly remove a static Pod you must delete the manifest file. - The mirror Pod is visible in whatever namespace the kubelet reports it under (defaults to
defaultunless the manifest setsmetadata.namespace).
# The mirror Pod appears with the node name appended
kubectl get pods
# NAME READY STATUS RESTARTS AGE
# static-web-controlplane 1/1 Running 0 30s
# Deleting the mirror does NOT remove the static Pod — the kubelet recreates it
kubectl delete pod static-web-controlplane
kubectl get pods # it's back within seconds
How do you know a Pod is static and not a regular Pod? Two tells:
# 1. The node name is appended to the Pod name
# 2. ownerReferences points to a Node, not a ReplicaSet/Job
kubectl get pod static-web-controlplane -o jsonpath='{.metadata.ownerReferences[0].kind}'
# Node
A regular Pod is owned by a controller (or nothing); a static Pod’s mirror is owned by the Node. That ownerReferences: Node is the cleanest programmatic signal.
Why the Control Plane Runs as Static Pods
This is the “aha” that ties everything together. On a kubeadm-built cluster, run:
ls /etc/kubernetes/manifests/
# etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
The four core control-plane components are static Pods. This is a deliberate bootstrapping solution to a chicken-and-egg problem: the API server can’t be scheduled by the scheduler, because the scheduler needs the API server to exist first. Static Pods break the cycle. The kubelet — which runs as a plain systemd service, not a Pod — reads these manifests off disk and starts the control plane without needing any control plane to already be running.
That design has direct exam consequences:
- To change an API server flag (say, enable an admission plugin or fix an audit log path), you edit
/etc/kubernetes/manifests/kube-apiserver.yamland save. The kubelet detects the change and restarts the Pod automatically. There is nokubectl editfor this — the file is authoritative. - If you make a typo in that manifest, the API server won’t come back, and
kubectlstops responding entirely. You then have to fix the file on the node directly (see troubleshooting below). This is a very common way candidates lose time — and points — on exam day. - During a cluster upgrade,
kubeadmswaps the image tags in these manifests; understanding that they’re static Pods explains why the components restart on their own. - The etcd backup and restore workflow reads connection details (certs, endpoints) straight out of
etcd.yaml, because that manifest is the ground truth for how etcd is running.
Editing a Control-Plane Static Pod Safely
The canonical task: “add --enable-admission-plugins=NodeRestriction,PodSecurity to the API server.” The workflow:
# 1. Back up the manifest first — always
cp /etc/kubernetes/manifests/kube-apiserver.yaml ~/kube-apiserver.yaml.bak
# 2. Edit it in place
vi /etc/kubernetes/manifests/kube-apiserver.yaml
# add or modify the flag under spec.containers[0].command
# 3. Save. The kubelet notices the file change and restarts the Pod.
# Watch for the API server to come back (kubectl may error briefly):
watch crictl ps # container runtime view, works without the API server
kubectl get pods -n kube-system | grep apiserver
Two things that trip people up:
- The restart is not instant. When you save changes to
kube-apiserver.yaml, the old Pod is torn down and a new one starts. For 10–30 secondskubectlreturns connection errors. That’s expected — wait, don’t panic-edit. - Use
crictl, notkubectl, to observe the control plane while the API server is down. Because static Pods run under the container runtime directly,crictl psandcrictl logssee them even when the API server can’t answer.
# Inspect control-plane containers directly via the runtime
crictl ps
crictl logs <container-id>
Static Pods vs DaemonSets: Choosing the Right Tool
Both run “one Pod per node,” so the exam likes to test whether you know when each applies.
| Aspect | Static Pod | DaemonSet |
|---|---|---|
| Managed by | kubelet, from a file on disk | DaemonSet controller, via API server |
| Needs API server running? | No | Yes |
| Scheduling | None — bound to the node with the file | Scheduler-assisted, respects taints/tolerations |
| How you deploy it | Place a manifest in staticPodPath on each node | kubectl apply one object, cluster-wide |
| Scope | One node | All matching nodes automatically |
| Self-heals across nodes | No | Yes |
| Typical use | Bootstrapping the control plane | Node agents: log shippers, CNI, kube-proxy, monitoring |
The rule of thumb: use a static Pod when you need something to run before or independently of the control plane (i.e. the control plane itself), and a DaemonSet for everything else you want on every node. For a refresher on how DaemonSets and the other workload controllers behave, see the KCNA workloads & controllers guide. For a broader look at how ordinary Pods land on nodes, the CKA workloads & scheduling guide covers the scheduler path that static Pods deliberately bypass.
Creating and Removing Static Pods on a Worker Node
The exam sometimes asks you to create a static Pod on a specific worker node — not the control plane. The steps are the same, but you must do them on that node, because the manifest path is local:
# SSH to the target node first (the exam gives you node access)
ssh node01
# Confirm the path this node's kubelet watches
grep staticPodPath /var/lib/kubelet/config.yaml
# Create the manifest there
cat <<'EOF' > /etc/kubernetes/manifests/static-app.yaml
apiVersion: v1
kind: Pod
metadata:
name: static-app
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]
EOF
# Verify from the control plane via the mirror Pod
kubectl get pods -o wide | grep static-app
# static-app-node01 1/1 Running ... node01
To remove it, delete the file on that node — not the mirror Pod:
ssh node01 rm /etc/kubernetes/manifests/static-app.yaml
# The kubelet stops the Pod and the mirror disappears
If you delete the mirror Pod with kubectl but leave the file, you’ll waste time watching it respawn. Delete the file, every time.
Troubleshooting Static Pods
When a static Pod won’t start — or a control-plane component is down — walk this checklist. It overlaps with the general CKA troubleshooting guide, but static Pods have their own failure modes.
1. Confirm you’re editing the right path on the right node. A static Pod only starts if its file is in that node’s staticPodPath. Editing /etc/kubernetes/manifests on the control plane does nothing for a worker.
grep staticPodPath /var/lib/kubelet/config.yaml
ls -l /etc/kubernetes/manifests/
2. Validate the manifest. A YAML syntax error or invalid field means the kubelet silently refuses to start the Pod, and no mirror appears. There’s no kubectl apply error to guide you because you never applied it.
# Check kubelet logs for parse/validation errors
journalctl -u kubelet -f | grep -i static
3. When the API server itself is broken, kubectl is useless. Drop to the container runtime:
crictl ps -a # list containers incl. exited
crictl ps -a | grep apiserver
crictl logs <apiserver-container> # find the actual error (bad flag, cert path, etc.)
A common cause is a bad edit to kube-apiserver.yaml: a mistyped flag, a wrong volume hostPath, or an admission plugin name that doesn’t exist. Restore your backup and re-apply the change carefully:
cp ~/kube-apiserver.yaml.bak /etc/kubernetes/manifests/kube-apiserver.yaml
# kubelet restarts the Pod from the good file
4. Restarts and the kubelet. If a static Pod change doesn’t take effect, the kubelet may need a nudge (rare, but it appears in troubleshooting scenarios):
systemctl status kubelet
systemctl restart kubelet # only if the kubelet itself is misbehaving
Keep the kubectl cheat sheet open for the -o wide / -o jsonpath flags that identify mirror Pods quickly.
Common Exam Traps
- Deleting the mirror Pod doesn’t delete the static Pod.
kubectl delete pod <name>-<node>is undone by the kubelet in seconds. Remove the manifest file instead. - Static Pods live on a specific node. To create one on
node01, you must write the file onnode01, not on the control plane. - The node name is appended to the Pod name.
static-webshows up asstatic-web-controlplane. If a question shows a Pod with a node-name suffix and no owning controller, it’s static. ownerReferencespoints to a Node. That’s the definitive signal a mirror Pod is backing a static Pod.- The control plane is static Pods. Fixing the API server means editing
/etc/kubernetes/manifests/kube-apiserver.yamland waiting for the automatic restart — usecrictlto watch whilekubectlis down. - Static Pods can’t use ServiceAccounts, ConfigMaps, or Secrets that require the API server at start in the same way controller-managed Pods do — they’re meant to be self-contained.
staticPodPathcomes from the kubelet config,/var/lib/kubelet/config.yaml, not from a flag on modern clusters.
Practice Static Pods Before Exam Day
Static Pods reward hands-on repetition: you should be able to create one on a named node, identify a mirror Pod, and fix a broken kube-apiserver.yaml with crictl without stopping to think about where the files live. The Certified Kubernetes Administrator (CKA) Mock Exam Bundle drills exactly these performance-based scenarios — manifest placement, mirror Pod identification, and recovering a downed control plane — in a realistic in-browser environment, with explanations that reinforce why each step works. Pair it with the CKA 30-Day Study Plan to turn “I’ve read about static Pods” into “I can recover the API server in two minutes.” For the surrounding cluster-lifecycle skills, the kubeadm installation and cluster upgrade guides are natural next reads.
Frequently Asked Questions
What is a static Pod in Kubernetes?
A static Pod is a Pod managed directly by the kubelet on a specific node, without the API server, scheduler, or any controller. The kubelet reads the Pod manifest from a local directory (staticPodPath, usually /etc/kubernetes/manifests) and runs it. It runs even when the control plane is down, which is why the control plane itself is built from static Pods.
Where is the static Pod manifest path?
On a kubeadm cluster it is /etc/kubernetes/manifests. The exact value is defined by staticPodPath in the kubelet configuration file at /var/lib/kubelet/config.yaml. Check it with grep staticPodPath /var/lib/kubelet/config.yaml.
What is a mirror Pod?
A mirror Pod is the read-only copy of a static Pod that the kubelet creates in the API server so it’s visible to kubectl get pods. Its name has the node name appended, and its ownerReferences point to the Node. You can’t manage the static Pod through the mirror — deleting the mirror just makes the kubelet recreate it.
Why does the Kubernetes control plane run as static Pods?
To solve a bootstrapping chicken-and-egg problem: the API server can’t be scheduled because scheduling requires the API server. Static Pods let the kubelet start the control plane directly from disk with nothing else running. That’s why etcd, kube-apiserver, kube-controller-manager, and kube-scheduler appear as manifests in /etc/kubernetes/manifests.
How do I delete a static Pod?
Delete its manifest file from the node’s staticPodPath directory. Running kubectl delete pod only removes the mirror Pod; the kubelet immediately recreates it because the file still exists.
What’s the difference between a static Pod and a DaemonSet?
A DaemonSet is managed by the DaemonSet controller through the API server and automatically runs one Pod on every matching node, self-healing across nodes. A static Pod is managed by a single node’s kubelet from a local file, runs only on that node, and needs no API server. Use static Pods for bootstrapping the control plane; use DaemonSets for node agents like log shippers and kube-proxy.
How do I fix a kube-apiserver that won’t start after editing its manifest?
Because kube-apiserver.yaml is a static Pod, kubectl won’t help while the API server is down. Use the container runtime: crictl ps -a | grep apiserver and crictl logs <container-id> to read the real error, then correct the manifest (or restore your backup) in /etc/kubernetes/manifests/. The kubelet restarts the Pod automatically once the file is valid.