Kubernetes interview questions
Kubernetes interview questions from fresher to 6+ years — pods and services, workloads and scheduling, networking, storage, RBAC, and control-plane architecture, each answer tagged by experience level.
Fresher 0–1y · Junior 1–3y · Mid 3–6y · Senior 6+y — every answer is tagged so you can prep for your level.
What is a Pod?Fresher
The smallest deployable unit — one or more containers sharing a network namespace (same IP and port space) and storage volumes, always scheduled together onto one node.
# one-off (debugging)
kubectl run web --image=nginx --restart=Never
# real workloads use a manifest
apiVersion: v1
kind: Pod
metadata: { name: web }
spec:
containers:
- name: web
image: nginx:1.27Deployment vs StatefulSet vs DaemonSet?Fresher
A Deployment runs interchangeable stateless replicas; a StatefulSet gives each replica a stable identity, ordered rollout, and its own PVC (databases); a DaemonSet runs one Pod per node (agents, log shippers).
kind: Deployment # stateless web/api — N identical pods kind: StatefulSet # db-0, db-1 … stable name + own volume kind: DaemonSet # exactly one pod per node (agent, CNI)
What does a Service do?Fresher
It gives a stable virtual IP and DNS name in front of a changing set of Pods selected by labels and load-balances to the ready ones. ClusterIP is internal, NodePort/LoadBalancer expose externally.
kubectl expose deploy web --port=80 --target-port=8080 # stable DNS: web.default.svc.cluster.local
ConfigMap vs Secret?Fresher
Both inject configuration as env vars or files. Secrets are for sensitive data — base64-encoded at rest (enable etcd encryption for real protection) and guarded with tighter RBAC.
kubectl create configmap app --from-literal=LOG_LEVEL=info kubectl create secret generic db --from-literal=PASSWORD=s3cr3t # base64 is NOT encryption — enable etcd encryption at rest
How do labels and selectors work?Junior
Labels are key/value tags on objects; selectors query them. Services, Deployments, and NetworkPolicies all target Pods by label selector rather than by name, which is what makes the model declarative.
kubectl get pods -l app=web,tier=frontend kubectl label pod web release=canary
ReplicaSet vs Deployment — who owns what?Junior
A ReplicaSet keeps N identical Pods running; a Deployment manages ReplicaSets to give you versioned rollouts and rollback. You almost always create a Deployment, never a bare ReplicaSet.
Each rollout creates a new ReplicaSet; the old one is kept (scaled to 0) so undo is instant.
What is a namespace and when do you use one?Junior
A virtual cluster scope for names, quotas, and RBAC. Use namespaces to separate teams/environments and apply ResourceQuota and LimitRange — but they are not a hard security boundary on their own.
kubectl create namespace team-a kubectl -n team-a get pods
How do you inspect why a resource is unhealthy?Fresher
Work from status to detail to logs: get for a summary, describe for events and conditions, and logs (with --previous) for the app output before a crash.
kubectl get pod web -o wide kubectl describe pod web # events are at the bottom kubectl logs web -c app --previous
How does a rolling update work?Junior
The Deployment creates a new ReplicaSet and shifts Pods gradually, honoring maxSurge/maxUnavailable so the app stays available. Readiness probes gate traffic to new Pods; `kubectl rollout undo` reverts.
maxSurge/maxUnavailable control how aggressive the swap is.
kubectl set image deploy/web app=web:v2 kubectl rollout status deploy/web kubectl rollout undo deploy/web # revert to previous ReplicaSet
Liveness vs readiness vs startup probe?Junior
Liveness restarts a wedged container; readiness removes a Pod from Service endpoints until it can serve (without killing it); startup protects slow-booting apps from premature liveness failures.
readinessProbe: # gate traffic in/out
httpGet: { path: /healthz, port: 8080 }
periodSeconds: 5
livenessProbe: # restart if wedged
httpGet: { path: /livez, port: 8080 }
failureThreshold: 3
startupProbe: # protect slow boots
httpGet: { path: /healthz, port: 8080 }
failureThreshold: 30Requests vs limits?Junior
Requests are what the scheduler reserves and uses for placement; limits cap actual usage. Exceeding a memory limit gets the container OOM-killed; CPU over the limit is throttled, not killed.
Requests also set the QoS class — Guaranteed when requests equal limits, which is the last to be evicted under pressure. Limits are enforced by cgroups: CPU throttles, memory over-limit triggers an OOM kill (exit 137).
resources:
requests: { cpu: "100m", memory: "128Mi" } # scheduling
limits: { cpu: "500m", memory: "256Mi" } # enforcementHow do taints, tolerations, and affinity steer scheduling?Mid
Taints repel Pods from nodes unless the Pod has a matching toleration; node/pod affinity and anti-affinity attract or separate Pods; topologySpreadConstraints balance replicas across zones/nodes.
A taint is a property of the node that repels; a toleration is the Pod opting in. Affinity/anti-affinity expresses soft (preferred) or hard (required) placement, and topology spread keeps replicas balanced across zones so one failure domain cannot take you down.
kubectl taint nodes gpu-1 gpu=true:NoSchedule
tolerations:
- key: gpu
operator: Equal
value: "true"
effect: NoScheduleHow does the HPA decide to scale?Mid
It samples a metric (CPU, memory, or custom/external via the metrics API) and adjusts replica count toward a target utilization, within min/max and a stabilization window to avoid flapping. VPA instead right-sizes requests.
Every ~15s it reads the metric and computes desiredReplicas = ceil(currentReplicas × currentValue / targetValue), clamped to min/max with a stabilization window so it does not thrash. It needs the metrics-server (or an adapter for custom/external metrics).
kubectl autoscale deploy/web --cpu-percent=70 --min=3 --max=20 kubectl get hpa web --watch
What is a PodDisruptionBudget for?Mid
It caps how many replicas can be voluntarily evicted at once (drains, upgrades), so a rolling node operation cannot take an app below its minimum available — it does not protect against involuntary disruption like a node crash.
A drain (node upgrade, kubectl drain) is a voluntary disruption and must respect the PDB; a kernel panic or preemption is involuntary and ignores it. Set minAvailable/maxUnavailable so an upgrade cannot take an app below quorum.
apiVersion: policy/v1
kind: PodDisruptionBudget
spec:
minAvailable: 2
selector: { matchLabels: { app: web } }Job vs CronJob?Mid
A Job runs one or more Pods to successful completion (batch work, DB migrations); a CronJob creates Jobs on a schedule. Bound them with backoffLimit and activeDeadlineSeconds so a failing Job does not loop forever.
kubectl create job db-migrate --image=migrate:1 kubectl create cronjob nightly --image=report:1 --schedule="0 2 * * *"
How does the scheduler place a Pod?Senior
In two phases — filtering, then scoring — then it binds the Pod to the winning node, which the kubelet then starts.
Filtering drops infeasible nodes (resources do not fit, taints not tolerated, node/affinity selectors, volume zone). Scoring ranks the survivors (least-allocated, affinity, topology spread) and the highest score wins. Scheduling only writes a binding — the kubelet on that node pulls and runs the containers.
How does a Service actually route to Pods?Mid
The endpoints controller tracks ready Pods behind the selector into EndpointSlices; kube-proxy (iptables/IPVS) or a CNI programs the node to DNAT the Service ClusterIP to a backend Pod IP.
The Service is only a stable VIP; the data path is programmed on every node by kube-proxy (iptables/IPVS) or by eBPF (Cilium). EndpointSlices list only Ready Pods, so an unready Pod receives no traffic.
kubectl get endpointslices -l kubernetes.io/service-name=web kubectl describe svc web | grep -i endpoints
Ingress vs Gateway API?Mid
Ingress is the older L7 HTTP router with controller-specific annotations. Gateway API is the successor — role-oriented (GatewayClass/Gateway/HTTPRoute), portable, and supports richer routing and non-HTTP protocols.
Ingress crammed everything into one object with vendor-specific annotations. Gateway API splits roles — infra owns GatewayClass/Gateway, app teams own HTTPRoute — and natively supports header/weight routing and TCP/gRPC.
# HTTPRoute — 90/10 canary
spec:
rules:
- backendRefs:
- { name: web-v1, port: 80, weight: 90 }
- { name: web-v2, port: 80, weight: 10 }What is a NetworkPolicy and its default behavior?Mid
A namespaced rule set restricting Pod traffic by label selector. Pods are non-isolated until a policy selects them; then only explicitly allowed ingress/egress passes — and it only works if the CNI enforces policies.
Policies are additive and become deny-by-default only for Pods they select. The usual pattern is a default-deny-ingress policy per namespace, then explicit allow rules — and it does nothing unless your CNI enforces policy.
kind: NetworkPolicy
spec:
podSelector: {} # every pod in the namespace
policyTypes: [Ingress] # …with no allow rule = deny all inExplain PV, PVC, and StorageClass.Mid
A PVC is a request for storage; a PV is the actual volume. A StorageClass enables dynamic provisioning so a PVC creates a PV on demand via a CSI driver, with a reclaim policy (Delete/Retain) controlling cleanup.
A PVC binds to a PV; with a StorageClass the CSI provisioner creates the PV on demand. volumeBindingMode: WaitForFirstConsumer delays that until a Pod is scheduled, so the volume lands in the same zone as the Pod.
kubectl get pvc,pv kubectl get storageclass
ClusterIP vs NodePort vs LoadBalancer vs ExternalName?Junior
ClusterIP is internal-only (the default); NodePort opens the same port on every node; LoadBalancer provisions a cloud LB in front of that; ExternalName is just a DNS CNAME to a host outside the cluster.
kubectl expose deploy web --type=ClusterIP --port=80 # internal kubectl expose deploy web --type=LoadBalancer --port=80 # public
How does DNS work inside the cluster?Senior
CoreDNS gives every Service an A record like `svc.ns.svc.cluster.local`; Pods resolve short names via the search domains in /etc/resolv.conf. Headless Services return Pod IPs directly for StatefulSet discovery.
CoreDNS runs as a Deployment behind a Service. Pod resolv.conf sets ndots:5 and search domains, which is why short names try namespace-scoped suffixes first (and can cause surprise extra lookups). Headless Services (clusterIP: None) return each Pod IP for StatefulSet discovery.
nslookup web.default.svc.cluster.local kubectl -n kube-system get pods -l k8s-app=kube-dns
How does RBAC work?Mid
Roles/ClusterRoles list allowed verbs on resources; RoleBindings/ClusterRoleBindings grant them to users, groups, or ServiceAccounts. Access is deny-by-default and additive — there are no deny rules.
A RoleBinding ties a subject (user, group, or ServiceAccount) to a Role (namespaced) or ClusterRole (cluster-wide). It is purely additive and deny-by-default — there are no deny rules — so you scope tightly and verify with kubectl auth can-i.
kubectl create role dev --verb=get,list --resource=pods kubectl create rolebinding dev --role=dev --serviceaccount=team-a:builder kubectl auth can-i list pods --as=system:serviceaccount:team-a:builder
What does Pod Security Admission enforce?Mid
Built-in admission applying the Pod Security Standards (privileged/baseline/restricted) per namespace via labels — blocking or warning on privileged containers, host namespaces, and dangerous capabilities.
Three standards (privileged / baseline / restricted) × three modes (enforce / audit / warn), set by namespace label. Roll out with warn+audit first to surface violations, then flip to enforce.
kubectl label ns team-a pod-security.kubernetes.io/enforce=restricted kubectl label ns team-a pod-security.kubernetes.io/warn=restricted
How do you protect Secrets at rest?Mid
Base64 is encoding, not encryption. Turn on etcd encryption-at-rest (ideally KMS-backed), tighten RBAC on secrets, and push high-value secrets to an external manager (Vault / External Secrets Operator).
Without an EncryptionConfiguration, anyone who reads the etcd data or a backup reads every secret in plaintext. A KMS provider keeps the key outside the cluster, so an etcd snapshot alone is useless to an attacker.
resources:
- resources: ["secrets"]
providers:
- kms: { name: cloudkms, endpoint: unix:///tmp/kms.sock }
- identity: {} # reads old plaintext during migrationHow should workloads authenticate to the API or cloud?Senior
Use a dedicated ServiceAccount with a short-lived projected token (bound to the Pod), and federate to cloud IAM via OIDC/workload identity instead of long-lived static keys mounted as Secrets.
Mount a projected ServiceAccount token — audience-bound and short-lived — and exchange it with the cloud IdP for temporary credentials (IRSA on EKS, Workload Identity on GKE). No static cloud key ever lives in a Secret.
Name key securityContext hardening settings.Senior
runAsNonRoot, a non-zero runAsUser, readOnlyRootFilesystem, allowPrivilegeEscalation:false, drop ALL capabilities (add back only what is needed), and a seccompProfile of RuntimeDefault.
securityContext:
runAsNonRoot: true
runAsUser: 10001
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities: { drop: ["ALL"] }
seccompProfile: { type: RuntimeDefault }How do admission controllers extend cluster policy?Senior
Validating/Mutating webhooks intercept API requests after authn/authz — mutating can inject sidecars or defaults, validating can reject non-compliant objects. Gatekeeper and Kyverno build policy engines on this.
A request flows through mutating webhooks (inject sidecars, defaults) then validating webhooks (reject non-compliant) before it is written. Kyverno/Gatekeeper plug in here — but a broken webhook can block the whole API, so set failurePolicy, timeouts, and namespace exclusions carefully.
What are the control-plane components?Mid
kube-apiserver (the front door and only thing that talks to etcd), etcd (the store), kube-scheduler (placement), kube-controller-manager (reconcile loops), and cloud-controller-manager; nodes run kubelet and kube-proxy.
Only the apiserver talks to etcd; everything else watches the apiserver. The scheduler and controllers are level-driven reconcilers comparing desired vs actual — which is exactly why the system self-heals.
Why does etcd matter and how do you protect it?Senior
etcd is the single source of truth for all cluster state — lose it and you lose the cluster. Protect it with TLS + peer auth, encryption at rest, strict firewalling, and regular snapshots.
It is a Raft quorum store: run an odd number of members (3 or 5), never even, and keep it on low-latency disks because every write is fsynced across the quorum.
ETCDCTL_API=3 etcdctl snapshot save snap.db etcdctl snapshot status snap.db -w table
Walk through what happens on `kubectl apply`.Senior
kubectl sends the object to the apiserver, which authenticates, authorizes (RBAC), runs admission, validates, and persists to etcd. Controllers notice the new desired state and reconcile it; the scheduler binds Pods to nodes and kubelets start them.
apply does a three-way merge (last-applied vs live vs your file) so it is declarative and non-destructive. The object is authenticated, authorized, admitted, validated, and stored; then controllers reconcile toward it and the scheduler binds any new Pods.
kubectl diff -f app.yaml # what will change kubectl apply -f app.yaml --server-side
A Pod is CrashLoopBackOff — how do you debug?Mid
kubectl describe for events and last state, kubectl logs --previous for the crashed process, check the exit code, probes, resource limits (OOM), config/secret mounts, and image entrypoint. Reproduce with an ephemeral debug container if needed.
CrashLoop means the container keeps exiting and the kubelet backs off (10s → 20s → 40s …). The exit code points the way: 137 = OOM/killed, 143 = SIGTERM, 1 = app error.
kubectl describe pod web kubectl logs web --previous kubectl debug web -it --image=busybox --target=app
A Service returns no endpoints — how do you fix it?Mid
Almost always a selector/label mismatch or Pods failing readiness — only Ready Pods whose labels match the Service selector appear in the EndpointSlice.
kubectl get endpoints web # empty? kubectl get pods --show-labels kubectl describe svc web | grep Selector
A node goes NotReady — what happens and how do you investigate?Senior
The node stops heartbeating; after the eviction timeout the controller reschedules controller-owned Pods elsewhere (bare Pods are lost). Check kubelet status, disk/memory pressure, container runtime, and network to the apiserver.
The kubelet stops posting status; after node-monitor-grace-period the node is marked NotReady and, after the eviction timeout, controller-owned Pods are rescheduled. Usual causes: kubelet/CRI down, disk or memory pressure, or lost apiserver connectivity.
kubectl describe node n1 | grep -A6 Conditions kubectl get pods -A -o wide --field-selector spec.nodeName=n1
How do you design multi-tenancy on a shared cluster?Senior
Namespace-per-tenant with ResourceQuota/LimitRange, RBAC scoped per namespace, NetworkPolicies for isolation, Pod Security/admission policy, and separate node pools or virtual clusters for hard isolation — soft multi-tenancy alone is not a trust boundary.
Soft multi-tenancy (namespace + RBAC + quota + NetworkPolicy + PSA) is fine for trusted teams. Hostile tenants need hard isolation — separate node pools, virtual clusters (vCluster), or separate clusters — because they still share one kernel and one apiserver.
kubectl create resourcequota team-a --hard=cpu=20,memory=40Gi,pods=50 -n team-a