All resources
Kubernetes · Interview prep

Kubernetes interview questions

Prep from Pod and Service basics through Pending triage, NetworkPolicy default-deny, etcd encryption, and multi-tenant isolation. Answers are leveled Beginner → Expert so you can practice at the depth your interview needs.

Filter

Beginner → Intermediate → Advanced → Expert — answers are written the way you’d say them in a real interview. Advanced and Expert go deeper with war-story detail, architecture diagrams, and the follow-up an interviewer often asks next.

Fundamentals
What is a Pod?Beginner

A Pod is basically the smallest thing you schedule in Kubernetes — one or more containers that share a network namespace and volumes, so they always land on the same node and can talk over localhost.

Create a Pod
# one-off (debugging)
kubectl run web --image=nginx --restart=Never

# real workloads use a controller, not a bare Pod
apiVersion: v1
kind: Pod
metadata: { name: web }
spec:
  containers:
    - name: web
      image: nginx:1.27
What does a Service do?Beginner

A Service gives you a stable virtual IP and DNS name in front of Pods that keep coming and going. It only load-balances to Ready backends. ClusterIP stays internal; NodePort and LoadBalancer are how you expose things outside.

Expose a Deployment
kubectl expose deploy web --port=80 --target-port=8080
# DNS: web.<namespace>.svc.cluster.local
Deployment vs StatefulSet vs DaemonSet — when do you use each?Beginner

I'd reach for a Deployment for interchangeable stateless replicas. StatefulSet when each replica needs a stable name, ordered rollout, and its own PVC — think databases. DaemonSet when I want exactly one Pod per eligible node, like agents or CNI.

Pick the right controller
kind: Deployment   # stateless web/api — N identical pods
kind: StatefulSet  # db-0, db-1 … stable DNS + own volume
kind: DaemonSet    # exactly one pod per node (agent, CNI)
ConfigMap vs Secret?Beginner

Both inject config as env vars or files. I'd put sensitive stuff in a Secret — but I'd also say out loud that base64 in etcd isn't encryption. Real protection is tight RBAC plus encryption at rest.

Create config + secret
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?Beginner

Labels are just key/value tags on objects; selectors query them. Services, Deployments, NetworkPolicies, PDBs — they all target Pods by label, not by name. That's what makes the model declarative.

Select by label
kubectl get pods -l app=web,tier=frontend
kubectl label pod web release=canary
What is a namespace and when do you use one?Beginner

It's a virtual cluster scope for names, quotas, NetworkPolicies, and RBAC. I'd use namespaces to separate teams or environments — but I wouldn't treat a namespace alone as a hard security boundary. Pair it with RBAC, quotas, and NetworkPolicy.

Create and use a namespace
kubectl create namespace team-a
kubectl -n team-a get pods
Job vs CronJob?Beginner

A Job runs Pods until they succeed — migrations, batch work. A CronJob just creates Jobs on a schedule. I'd always bound failing Jobs with backoffLimit and activeDeadlineSeconds so they don't retry forever.

One-off + scheduled
kubectl create job db-migrate --image=migrate:1
kubectl create cronjob nightly --image=report:1 --schedule="0 2 * * *"
ClusterIP vs NodePort vs LoadBalancer vs ExternalName?Beginner

ClusterIP is internal-only — that's the default. NodePort opens the same high port on every node. LoadBalancer puts a cloud LB in front of that. ExternalName is just a DNS CNAME to something outside the cluster; there's no proxying.

Pick a Service type
kubectl expose deploy web --type=ClusterIP --port=80
kubectl expose deploy web --type=LoadBalancer --port=80
How do you inspect why a resource is unhealthy?Beginner

I'd start with get for the summary, then describe so I can read events and conditions, then logs — and logs --previous if it just crashed. That trio covers most of what I need before I go deeper.

Daily triage trio
kubectl get pod web -o wide
kubectl describe pod web        # events at the bottom
kubectl logs web -c app --previous
ReplicaSet vs Deployment — who owns what?Intermediate

A ReplicaSet just keeps N identical Pods running. A Deployment owns ReplicaSets so you get versioned rolling updates and rollback. In practice I'd almost always create a Deployment, never a bare ReplicaSet.

DeploymentReplicaSetPods

Each rollout creates a new ReplicaSet; the previous one is kept (often scaled to 0) so undo is instant.

Rollout ownership
kubectl get deploy,rs,pods -l app=web
kubectl rollout history deploy/web
How does a Service actually route traffic to Pods?Intermediate

The endpoints controller tracks Ready Pods matching the selector into EndpointSlices. Then kube-proxy — or an eBPF CNI — programs each node to DNAT the Service ClusterIP to a backend Pod IP. If a Pod isn't Ready, it never shows up in that slice.

Service ClusterIPEndpointSlice (Ready pods)kube-proxy / eBPFDNAT to Pod IP

Unready Pods never appear in the EndpointSlice, so readiness is the real traffic gate.

See the real backends
kubectl get endpointslices -l kubernetes.io/service-name=web
kubectl describe svc web | grep -i endpoints
Explain PV, PVC, and StorageClass.Intermediate

A PVC is the request for storage; a PV is the actual volume. StorageClass is how you get dynamic provisioning through a CSI driver. And the reclaim policy — Delete or Retain — decides what happens when the PVC goes away.

PVC (request)StorageClass + CSIPV provisionedMounted into Pod

WaitForFirstConsumer delays binding until a Pod is scheduled so the volume lands in the same zone.

Inspect storage
kubectl get pvc,pv
kubectl get storageclass
Workloads & scheduling
How does a rolling update work on a Deployment?Intermediate

The Deployment spins up a new ReplicaSet and shifts Pods gradually, respecting maxSurge and maxUnavailable. Readiness is what actually gates traffic onto the new Pods. If something goes sideways, kubectl rollout undo takes me back to the previous ReplicaSet.

New ReplicaSetSurge extra PodsReadiness gateScale down oldDone

maxSurge / maxUnavailable control how aggressive the swap is.

Rollout + rollback
kubectl set image deploy/web app=web:v2
kubectl rollout status deploy/web
kubectl rollout undo deploy/web
Liveness vs readiness vs startup probe — what does each do?Intermediate

Readiness pulls a Pod out of Service endpoints until it can serve — without killing it. Liveness restarts a wedged container. Startup is the shield for slow boots so liveness doesn't kill you during init. I'd never point liveness at a shared dependency like the DB.

startupProbereadinessProbe gates trafficlivenessProbe restarts if wedged

Never point liveness at a dependency (DB, cache) — a dependency outage will thrash-restart every Pod.

Probe config
readinessProbe:
  httpGet: { path: /healthz, port: 8080 }
  periodSeconds: 5
livenessProbe:
  httpGet: { path: /livez, port: 8080 }
  failureThreshold: 3
startupProbe:
  httpGet: { path: /healthz, port: 8080 }
  failureThreshold: 30
Requests vs limits — what does each control?Beginner

Requests are what the scheduler reserves for placement and QoS. Limits are the cgroup caps: CPU over the limit gets throttled, memory over the limit gets OOM-killed — that's your exit 137.

Set requests and limits
resources:
  requests: { cpu: "100m", memory: "128Mi" }
  limits:   { cpu: "500m", memory: "256Mi" }
How do taints, tolerations, and affinity steer scheduling?Intermediate

Taints push Pods away unless they carry a matching toleration. Affinity and anti-affinity pull Pods together or apart. topologySpreadConstraints are what I'd use to balance replicas across failure domains.

taint on nodetoleration on Podaffinity / spread refine placement
Taint a node and tolerate it
kubectl taint nodes gpu-1 gpu=true:NoSchedule

tolerations:
  - key: gpu
    operator: Equal
    value: "true"
    effect: NoSchedule
How does the Horizontal Pod Autoscaler decide to scale?Intermediate

It samples a metric — CPU, memory, or custom via the metrics API — and computes desiredReplicas from current times currentMetric over target, then clamps to min/max. There's a stabilization window so it doesn't flap every minute.

metrics-server / adapterHPA reads utilizationCompute desired replicasScale the Deployment
Autoscale on CPU
kubectl autoscale deploy/web --cpu-percent=70 --min=3 --max=20
kubectl get hpa web --watch
A Deployment rollout is stuck at 50% — how do you debug it?Advanced

I'd check rollout status and events first. Usually the new Pods never become Ready — ImagePullBackOff, bad probes, or a PDB / maxUnavailable blocking progress. I'd fix the new ReplicaSet or roll back; I wouldn't just raise surge and hope.

A rolling update only moves forward when new Pods become Ready. If readiness never passes, the Deployment stalls on purpose and old Pods keep serving. I'd describe the new Pods, look for ImagePullBackOff and probe failures, and check whether a PDB is blocking voluntary eviction of the old set. kubectl rollout undo is the safe move while I fix the image or probe.

Rolling update stalls when readiness never passes

New ReplicaSet surges; traffic only moves after readiness. Broken probes freeze the rollout.

Service routes to Ready pods only Deployment web · rolling update owns 2 ReplicaSets maxSurge = 1 (one extra pod) maxUnavailable = 0 (never dip) ReplicaSet v1 old · scaling 3 → 1 ReplicaSet v2 new · scaling 0 → 2 readiness gate 2 terminating · 1 Ready 1 starting · 1 Ready broken v2 fails readiness → rollout pauses · v1 keeps serving
live traffic → Ready pods only scaling up (new pods starting) draining (old pods terminating)
Unstick a rollout
kubectl rollout status deploy/web
kubectl describe deploy web | tail -40
kubectl get pods -l app=web -o wide
kubectl rollout undo deploy/web

Interviewer often follows with: Would you raise maxUnavailable to force progress, and what could go wrong?

What is a PodDisruptionBudget actually protecting?Advanced

It caps how many replicas can be voluntarily evicted at once — drains, node upgrades, that kind of thing — so you don't take an app below minAvailable. It doesn't protect against a node crash or an OOM kill. Interviewers love catching that myth.

kubectl drain and cluster autoscaler scale-down are voluntary, so they have to respect the PDB. A kernel panic, hardware failure, or preemption is involuntary and ignores it. I'd set minAvailable or maxUnavailable so upgrades can't break quorum, and I'd size replica count so the PDB is actually achievable — minAvailable: 2 with only two replicas blocks every drain.

Guarantee two stay up
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata: { name: web }
spec:
  minAvailable: 2
  selector: { matchLabels: { app: web } }

Interviewer often follows with: What happens if minAvailable equals your replica count during a drain?

HPA vs VPA vs Cluster Autoscaler — how do they differ, and how do they interact?Advanced

HPA changes replica count from metrics. VPA right-sizes requests and limits. Cluster Autoscaler adds or removes nodes when Pods can't schedule or nodes are empty. They solve different bottlenecks — and they can fight if you wire them carelessly.

HPA needs headroom on existing nodes; if every node is full, Pending Pods stay Pending until CA (or a human) adds capacity. VPA in update mode rewrites requests and may recreate Pods — pointing it at the same CPU metric as HPA without care causes oscillation. What I'd run in production: HPA on custom or CPU for replica count, VPA in recommendation mode or on memory only, CA watching unschedulable Pods. And I'd keep HPA minReplicas high enough for the PDB.

HPA scales replicas from metrics

CA scales nodes; VPA scales requests. Do not point HPA and VPA at the same metric blindly.

Metrics Server CPU / memory HPA controller targetUtilization × requests Deployment replicas N → M ! No requests = stuck utilization = usage / request missing request → 0% HPA sits at minReplicas 1 Scale the Deployment never scale Pods directly min / max + behavior windows stabilization avoids flapping 2 Verify kubectl top + HPA TARGETS load test until scale-up alert when pinned at max HPA reads metrics and patches Deployment.spec.replicas. Requests make utilization real. metrics → HPA → Deployment replicas → Pods
Metrics — serverHPA — decideScale — Deployment
Check autoscalers
kubectl get hpa,vpa -A
kubectl describe hpa web
kubectl get nodes -o wide
kubectl -n kube-system logs -l app=cluster-autoscaler --tail=50

Interviewer often follows with: Why can HPA look healthy while users still see Pending Pods?

Walk through how the kube-scheduler places a Pod — filter then score.Expert

Filtering drops nodes that can't take the Pod. Scoring ranks what's left. Highest score wins, the scheduler writes a binding, and the kubelet on that node pulls and runs the containers. Scheduling itself never starts containers — it only picks the node.

Filter plugins remove nodes that lack resources, don't match nodeSelector or affinity, have untolerated NoSchedule taints, or can't satisfy volume topology. Score plugins then rank the rest — ImageLocality, NodeResourcesFit, inter-pod affinity, topology spread — and the binder persists the nodeName. If every node fails filter, the Pod stays Pending with FailedScheduling events. Preemption is a separate path after a failed attempt, where a higher-priority Pod can evict lower-priority ones to make room.

Pending PodFilter feasible nodesScore & pick bestBind to nodekubelet runs it
Read why it did not schedule
kubectl describe pod web | grep -A20 Events
kubectl get events --field-selector involvedObject.name=web

Interviewer often follows with: Name two filter failures that leave a Pod Pending forever.

Blue-green on Kubernetes: how do you cut traffic over, and how do you roll back?Advanced

I'd run two Deployments behind one Service. When green looks good, I flip the Service selector — or Gateway weight — to green, and I keep blue scaled up so rollback is instant.

Unlike a rolling update, blue-green keeps the old version fully intact until you deliberately shift traffic. The cutover is a selector or route change, not another kubectl set image under fire. Schema migrations that aren't backward-compatible will break a naive blue-green — I'd plan expand/contract migrations separately. And I'd document rollback as a one-liner that retargets the Service to blue.

Blue-green cutover by Service selector

Keep blue warm until green proves healthy; rollback is a selector flip.

Blue (live) current production pods labeled env=blue Service / Ingress selector flip = cutover Green (idle) new version, no traffic smoke then switch 1 Cutover point selector at green 100% traffic in one step 2 Watch errors · latency · saturation soak window before teardown 3 Rollback flip selector back to blue instant — no rebuild Keep blue idle until green proves stable. Rollback is routing, not redeploy. blue ←→ Service selector ←→ green
Blue — liveGreen — candidateRollback — flip back
Cut over and roll back
kubectl patch svc web -p '{"spec":{"selector":{"app":"web","version":"green"}}}'
# rollback
kubectl patch svc web -p '{"spec":{"selector":{"app":"web","version":"blue"}}}'

Interviewer often follows with: How do you handle a non-backward-compatible database migration?

Probes misconfigured in production — what failure modes do you watch for?Advanced

Liveness hitting a shared dependency causes restart storms. Aggressive liveness on a slow GC pause kills healthy Pods. Missing readiness ships traffic to half-booted processes. Startup probe is usually the fix for slow boots.

Readiness should mean "I can serve this request now." Liveness should mean "I'm wedged and need a process restart" — a narrow condition. If /healthz opens a DB connection, a database blip restarts every Pod and makes the outage worse. I'd keep liveness shallow — process up — and put the deeper checks on readiness. I'd tune failureThreshold and periodSeconds from real latency, and pair with a startupProbe so cold caches don't trip liveness during boot.

Readiness gates traffic; liveness restarts

Startup shields slow boots. Never couple liveness to downstream deps.

kubelet runs probes on a schedule 1 Readiness can this pod take traffic? fail → drop from Endpoints no container restart deps / warm caches OK here 2 Liveness is the process deadlocked? fail → kubelet restarts it drops in-flight work never ping shared deps 3 Startup still booting? hold off delays readiness + liveness until success or timeout JVM / .NET slow boots Swap readiness and liveness and a DB blip becomes a fleet-wide restart. separate paths: /ready · /healthz · startupProbe window
Readiness — traffic gate Liveness — restart trigger Startup — boot shield
Safer probe shape
startupProbe:
  httpGet: { path: /livez, port: 8080 }
  failureThreshold: 30
  periodSeconds: 5
livenessProbe:
  httpGet: { path: /livez, port: 8080 }
  failureThreshold: 3
readinessProbe:
  httpGet: { path: /readyz, port: 8080 }
  periodSeconds: 5

Interviewer often follows with: Show me a readiness check that would incorrectly remove every Pod during a dependency outage.

Networking & storage
Ingress vs Gateway API — what is the difference?Intermediate

Ingress is the older L7 HTTP router, and you often end up in controller-specific annotations. Gateway API is the successor — role-oriented with GatewayClass, Gateway, HTTPRoute — more portable, with native weighted routing and non-HTTP routes.

ClientGateway / IngressServiceReady Pods
Weighted canary HTTPRoute
# HTTPRoute — 90/10 canary
spec:
  rules:
    - backendRefs:
        - { name: web-v1, port: 80, weight: 90 }
        - { name: web-v2, port: 80, weight: 10 }
A Service has no endpoints — how do you find the mismatch?Intermediate

I'd almost always look for a selector/label mismatch or Pods failing readiness. Only Ready Pods whose labels match the Service selector show up in the EndpointSlice.

Service selectorPod labels match?Pods Ready?EndpointSlice populated
Find the mismatch
kubectl get endpointslices -l kubernetes.io/service-name=web
kubectl get pods --show-labels
kubectl describe svc web | grep Selector
What is a NetworkPolicy, and what is the default behavior?Advanced

It's a namespaced rule set that restricts Pod traffic by label selectors. Pods are wide open until a policy selects them — then only the flows you allow pass, and only if your CNI actually enforces NetworkPolicy.

Policies are additive. Select a Pod with an Ingress policy and list no allow rules, and that's deny-all ingress for that Pod. What I'd do in production is default-deny ingress — and often egress — per namespace, then explicit allows for the app and DNS. Without a policy-capable CNI like Calico or Cilium, the objects are theater. And egress deny without a DNS allow will break name resolution and look like a random outage.

Default-deny then allow specific flows

A Pod is open until selected; then deny-all except the rules you write. Allow DNS.

namespace: prod policy: default-deny ingress: from role=web egress :53 web pod role=web · matched reporting pod role=reporting · no rule public internet 0.0.0.0/0 · external CoreDNS kube-dns · UDP/TCP 53 payments app=payments policy target · deny-all + allow A pod is unrestricted until a policy selects it — then it is deny-all except the explicit allows.
Allowed — web ingress reaches payments Blocked — stopped at the default-deny boundary DNS — egress to CoreDNS on :53
Default-deny ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: default-deny-ingress }
spec:
  podSelector: {}
  policyTypes: [Ingress]
# then add allow policies for app + DNS

Interviewer often follows with: Why did DNS break after you applied default-deny egress?

How does cluster DNS work, and what trips people up in production?Expert

CoreDNS serves records like svc.ns.svc.cluster.local. Pods resolve short names through search domains in resolv.conf. Headless Services return Pod IPs, which is what StatefulSets lean on for peer discovery.

Pod resolv.conf usually sets ndots:5 and several search domains, so short names try multiple suffixes before the bare name — that means surprising latency and extra NXDOMAINs under load. NetworkPolicies that deny egress to kube-system DNS break resolution for that whole namespace. Headless Services skip the VIP and return Pod IPs — StatefulSets need that for peers like db-0.db. In large clusters I'd tune CoreDNS replicas and watch for conntrack saturation on the node DNS path.

Resolve and inspect DNS
kubectl -n kube-system get pods -l k8s-app=kube-dns
kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup web.default.svc.cluster.local

Interviewer often follows with: What does ndots:5 change about how short names resolve?

How would you add mTLS between Pods without rewriting the apps?Expert

I'd terminate TLS in a sidecar or CNI mesh — Istio, Linkerd, Cilium — so apps speak plaintext on localhost while the proxy mutually authenticates and encrypts on the wire.

The data plane injects a proxy next to each Pod, or uses eBPF. Certificates come from a mesh CA with short rotation. Policies decide PERMISSIVE vs STRICT mTLS. You pay some hop latency and ops cost; you get cryptographic peer identity beyond NetworkPolicy IP rules. NetworkPolicy still matters — mTLS authenticates peers but doesn't replace allow-lists for who may talk. Failure modes I've hit: sidecars not injected, clock skew breaking certs, and STRICT mode during a partial rollout black-holing pods that aren't ready yet.

Sidecars encrypt pod-to-pod traffic

Apps stay plaintext-unaware; proxies mutually authenticate on the wire.

POD A POD B Mesh CA / SPIFFE issues short-lived SVID certs app unaware sidecar proxy Envoy · mTLS sidecar proxy Envoy · mTLS app unaware mTLS encrypted both present + verify certs SVID SVID plaintext plaintext
plaintext app ↔ sidecar (in-pod) encrypted mTLS tunnel between sidecars cert issuance SPIFFE / mesh CA
Check mesh / identity
kubectl get peerauthentication -A
kubectl get destinationrule -A
# Linkerd: linkerd viz edges deploy/web

Interviewer often follows with: Does mTLS replace NetworkPolicy? Why or why not?

Security
How does Kubernetes RBAC work?Intermediate

Roles and ClusterRoles list allowed verbs on resources. RoleBindings and ClusterRoleBindings grant them to users, groups, or ServiceAccounts. Access is deny-by-default and purely additive — there aren't deny rules.

SubjectRoleBindingRole (verbs × resources)Allow (else denied)
Grant and test least privilege
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
How do you apply RBAC least privilege for a CI deploy ServiceAccount?Advanced

I'd give it only the verbs and resources that pipeline actually needs in that namespace — typically get/list/watch/patch/update on specific Deployments — not cluster-admin, and not secrets get unless there's a real reason.

I'd start from the pipeline's actual API calls and write a Role that covers only those. Prefer namespaced RoleBindings over ClusterRoles. Avoid wildcards on resources and verbs. Keep image-pull secrets separate from deploy credentials. Then I'd verify with kubectl auth can-i --list as the ServiceAccount. Binding cluster-admin to a CI SA "just for Helm" is a classic interview fail — and a classic real-world mistake.

Tight deploy Role
kubectl create role deploy --verb=get,list,watch,patch,update \
  --resource=deployments,deployments/status -n team-a
kubectl create rolebinding ci-deploy --role=deploy \
  --serviceaccount=team-a:ci -n team-a
kubectl auth can-i --list --as=system:serviceaccount:team-a:ci -n team-a

Interviewer often follows with: Should this SA also get secrets get? When is that justified?

What does Pod Security Admission enforce?Advanced

It's built-in admission applying Pod Security Standards — privileged, baseline, restricted — per namespace via labels. It blocks or warns on privileged containers, host namespaces, and dangerous capabilities.

Three levels times three modes — enforce, audit, warn — set with namespace labels. I'd roll out warn and audit first so I see violations without breaking deploys, then flip enforce. restricted wants non-root, drop ALL capabilities, no hostPath/hostNetwork, and seccomp RuntimeDefault among other rules. PSA isn't a full policy engine — I'd use Gatekeeper or Kyverno for custom org rules — but it's the right default baseline for every tenant namespace.

PSS labels on the namespace

warn and audit first; enforce when the workload inventory is clean.

Namespace PSA labels Pod Security Admission built into apiserver Pod create admit or reject 1 Modes warn · audit · enforce start warn → then enforce exemptions for system NS 2 Levels privileged → baseline → restricted restricted = least privilege no root, no hostPath… 3 Roll out label per namespace fix dry-run before enforce fix workloads that break Modes × levels are independent. Enforce restricted only after warn is clean. namespace labels → PSA → admit/reject
Modes — warn/audit/enforceLevels — baseline/restrictedRollout — per NS
Enforce restricted
kubectl label ns team-a \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/audit=restricted \
  pod-security.kubernetes.io/warn=restricted

Interviewer often follows with: What breaks first when you enforce restricted on a legacy DaemonSet?

How do you protect Secrets at rest? Is base64 encryption?Advanced

No — base64 is just encoding. I'd enable EncryptionConfiguration on the API server, ideally with a KMS provider, tighten RBAC on secrets, and keep high-value material in an external manager synced in short-lived form.

Without encryption at rest, anyone who gets an etcd snapshot reads every Secret as base64-decoded plaintext. A KMS provider keeps the data-encryption key outside the cluster so the snapshot alone is useless. identity: {} as a trailing provider lets you read old plaintext during migration. Encryption at rest doesn't stop a privileged API client with secrets get — RBAC and external stores still matter.

API write becomes ciphertext in etcd

KMS keeps the key outside the cluster. Base64 was never encryption.

API write Secret object EncryptionConfiguration providers: aescbc / kms / identity etcd ciphertext ! Without encryption base64 in etcd = plaintext etcd backup = secret dump anyone with etcd access wins 1 Enable providers aescbc then kms preferred identity last for rollout restart apiserver 2 Rewrite + verify kubectl replace all Secrets etcdctl get → ciphertext then rotate / add KMS Base64 is encoding, not encryption. Prove ciphertext in etcd before you call it done. Secret → EncryptionConfiguration → etcd ciphertext
Risk — plaintext etcdEncrypt — providersVerify — rewrite
EncryptionConfiguration sketch
resources:
  - resources: ["secrets"]
    providers:
      - kms:
          name: cloudkms
          endpoint: unix:///var/run/kms.sock
      - identity: {}

Interviewer often follows with: Does encryption at rest stop a stolen kubeconfig with secrets get?

Name key securityContext hardening settings for a Pod.Beginner

runAsNonRoot, a non-zero runAsUser, readOnlyRootFilesystem, allowPrivilegeEscalation: false, drop ALL capabilities and add back only what you need, and seccompProfile RuntimeDefault. That's the checklist I'd recite in an interview.

Hardened securityContext
securityContext:
  runAsNonRoot: true
  runAsUser: 10001
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false
  capabilities: { drop: ["ALL"] }
  seccompProfile: { type: RuntimeDefault }
How do admission controllers like Gatekeeper extend cluster policy?Expert

Validating and mutating webhooks intercept API requests after authn/authz. Mutating injects defaults or sidecars; validating rejects non-compliant objects. Gatekeeper evaluates ConstraintTemplates against the request.

The path is authenticate → authorize → mutating admission → schema validation → validating admission → persist. A misconfigured webhook with failurePolicy=Fail and a bad selector can block namespace creation cluster-wide — I've seen that. Always set timeouts, narrow namespaceSelectors, and watch webhook latency. Gatekeeper is great for org policy like required labels or banned registries, but it's not a substitute for PSS on pod hardening. I'd start constraints in dry-run or warn mode.

Gatekeeper validating admission

Constraints evaluate after RBAC; a broken webhook can stall the API.

kubectl / CI create / apply kube-apiserver validating webhook ✓ ALLOW ✕ DENY 1 ConstraintTemplate Rego + CRD schema defines the policy language reusable across clusters 2 Constraint parameters + match which namespaces / kinds enforcementAction 3 dryrun first enforcementAction: dryrun audit without blocking then flip to deny Template = logic. Constraint = where it applies. Always dryrun before deny. request → webhook → template+constraint → allow/deny
Template — RegoConstraint — scopedryrun — then deny
See constraints
kubectl get constrainttemplates
kubectl get constraints -A
kubectl get validatingwebhookconfigurations

Interviewer often follows with: What is failurePolicy=Fail vs Ignore, and which do you choose for a security gate?

How should a Pod get secrets from Vault without a long-lived token?Expert

I'd use Kubernetes auth: the Pod presents its projected ServiceAccount JWT, Vault verifies it with the TokenReview API, and you get a short-lived Vault token bound to a role and policy.

Map Vault roles to ServiceAccount name plus namespace. Prefer projected tokens with audience and short expiration over the legacy secret-based SA token. I'd combine that with External Secrets Operator or the Vault Agent injector so the Kubernetes Secret — if you materialize one — is a cache, not the source of truth. Rotate Vault policies and Kubernetes Roles independently. Failure modes I've hit: wrong audience, TokenReview RBAC missing for Vault's SA, and clock skew.

Pod authenticates to Vault with an SA JWT

No bootstrap static token in the image or a mounted Secret.

Pod SA JWT kube-apiserver TokenReview Vault k8s auth method Secret short TTL 1 Login with SA token vault write auth/kubernetes/login role bound to SA + namespace Vault calls TokenReview 2 Policy + role read-only on one path never reuse cluster-admin audience-bound JWT preferred 3 Fetch + revoke Vault token → secret lease TTL or pod exit no long-lived static creds Identity is the SA JWT. Permissions are the Vault role — not the K8s ClusterRole. pod → TokenReview → Vault role → short-lived secret
Login — SA JWTAuthorize — Vault roleSecret — short TTL
Projected token + role sketch
# Pod uses projected SA token (audience=vault)
# Vault role bound to serviceaccountnames=app, serviceaccountnamespaces=team-a
kubectl exec deploy/app -- ls /var/run/secrets/tokens

Interviewer often follows with: Why is the legacy auto-mounted SA secret token a problem?

External Secrets Operator — what problem does it solve?Advanced

It keeps the source of truth in Vault or a cloud secret manager and materializes Kubernetes Secrets your Pods can mount, on a refresh interval — instead of copying secrets into Git or leaving long-lived values only in etcd.

SecretStore or ClusterSecretStore holds provider auth; ExternalSecret maps remote keys to a target Secret. You still need etcd encryption and RBAC because the materialized Secret is readable via the API. I'd treat ESO as sync, not a replacement for encryption or least privilege. And I'd watch for stale sync when the provider is down — pods may keep old values until refresh succeeds.

External store syncs into a Kubernetes Secret

Cluster holds a short-lived copy; Vault/cloud remains source of truth.

Vault / AWS external store External Secrets Operator SecretStore + ExternalSecret K8s Secret → Pod mount 1 SecretStore how to reach the backend auth: IRSA / Vault SA / keys cluster- or namespace-scoped 2 ExternalSecret which keys + refreshInterval maps remote → Secret data controller reconciles loop 3 Synced Secret native K8s Secret object mount / env as usual rotate at the store, not YAML ESO copies values into the cluster. The source of truth stays outside Kubernetes. store → SecretStore → ExternalSecret → Secret → Pod
Store — sourceESO — syncSecret — consume
Check sync status
kubectl get secretstores,externalsecrets -A
kubectl describe externalsecret app-db -n team-a

Interviewer often follows with: If someone can kubectl get secret, did ESO make that safe?

How do you verify image signatures before a Pod runs?Expert

I'd sign at build with cosign, and enforce verify in admission — Kyverno, Ratify, policy-controller — so unsigned or wrong-identity digests never schedule.

Tag mutability makes tag-only deploys unsafe — pin digests and verify the signature over that digest, and optionally a provenance attestation. Admission should fail closed for production namespaces. Keyless OIDC signing ties the identity to the CI workload, which is stronger than a shared static key in the pipeline. I'd pair that with a private registry allow-list so verification isn't bypassed by an alternate unsigned tag.

Sign at build; verify at admission

Verification happens before the Pod object is persisted.

CI build cosign sign Registry image + signature Admission cosign verify 1 Sign at build keyless OIDC or keypair attach to digest, not tag provenance travels with image 2 Store signatures OCI referrers / .sig same registry as the image immutable by digest 3 Verify on admit Kyverno / Gatekeeper / policy unsigned → reject Pod before it ever schedules Signing without admission is theater. Admission without signing has nothing to check. build → registry → verify → Pod create
Sign — CIStore — registryVerify — admission
Sign and verify
cosign sign --yes registry.example/app@sha256:...
cosign verify registry.example/app@sha256:...

Interviewer often follows with: Why is verifying a mutable tag weaker than verifying a digest?

What are common container escape paths on Kubernetes, and how do you close them?Expert

Privileged pods, hostPath mounts — especially docker.sock — host PID or network namespaces, and dangerous capabilities like SYS_ADMIN. I'd close them with PSS restricted, dropped caps, no host mounts, and runtime detection.

Containers share the host kernel — isolation is namespaces, cgroups, and seccomp, not a VM boundary. A writable hostPath to /var/run/docker.sock or / is effectively host root. --privileged disables most isolations. Defense in depth for me: PSA enforce=restricted, Gatekeeper bans on hostPath and privileged, read-only rootfs, seccomp RuntimeDefault, and Falco or Tetragon rules for unexpected shell or mount activity. Multi-tenant clusters with untrusted workloads need stronger isolation — gVisor, Kata, or separate clusters.

Shared kernel plus common misconfigurations

Privileged, host mounts, and leaked sockets are the usual doors.

Host kernel (shared) — escape target privileged or CAP_SYS_ADMIN hostPID / hostNetwork door #1 hostPath / /var/run/docker.sock write the host FS door #2 No seccomp or AppArmor/SELinux wide syscall surface door #3 Defenses PSS restricted drop caps · readOnly no host mounts One privileged pod with docker.sock is a host root foothold. Assume breakout is possible — shrink the blast radius with PSS + network policy + runtime detection. shared kernel + misconfig → escape → host close doors before you detect them
privileged — capshostPath — sockDefend — PSS
Hunt dangerous pods
kubectl get pods -A -o json | jq -r '
  .items[]
  | select(.spec.containers[]?.securityContext?.privileged==true
        or .spec.hostNetwork==true)
  | "\(.metadata.namespace)/\(.metadata.name)"'

Interviewer often follows with: Is a namespace enough isolation for an untrusted tenant?

How would you design soft multi-tenancy on one cluster (PSS + SA + NetworkPolicy)?Expert

Namespace per tenant with ResourceQuota and LimitRange, dedicated ServiceAccounts and namespaced RBAC, PSA enforce=restricted, and default-deny NetworkPolicies — and I'd be clear that soft multi-tenancy isn't a hostile-tenant boundary.

Trusted teams can share a cluster with namespace isolation plus quota, RBAC, PSA, and NetworkPolicy. Hostile or regulated tenants still share one kernel and one apiserver, so escape and noisy-neighbor risk remain — I'd use separate node pools, virtual clusters, or separate clusters for hard isolation. Disable automountServiceAccountToken where unused. Never give tenants cluster-admin or nodes/proxy. And I'd audit with Kubernetes audit logs and runtime detection.

Namespace + QuotaRBAC per SAPSS restrictedNetPol default-denyOptional node pool
Tenant baseline
kubectl create ns tenant-a
kubectl label ns tenant-a pod-security.kubernetes.io/enforce=restricted
kubectl create resourcequota t-a --hard=cpu=20,memory=40Gi,pods=50 -n tenant-a
# apply default-deny NetworkPolicy + allow DNS

Interviewer often follows with: When do you refuse soft multi-tenancy and split clusters?

Where does Falco (or similar) fit if admission already blocked bad Pods?Expert

Admission is preventative on the API object. Runtime detection watches syscalls and process behavior after the container starts — catching exploits admission can't see.

A compliant Pod can still get compromised via an app RCE. Falco or eBPF agents alert on unexpected shells, sensitive file reads, or breakout patterns. It doesn't replace PSS or NetworkPolicy; it closes the gap between "allowed to run" and "behaving as expected." I'd tune rules to cut noise, route alerts to on-call, and pair with network egress controls so a compromised pod can't freely exfiltrate.

Syscalls evaluated by rules become alerts

Runtime detection covers post-exploit behavior admission never sees.

Syscall eBPF / driver Falco rules engine condition → priority → output Alert SIEM / pager 1 Observe open, execve, connect… container + k8s metadata high volume, low context alone 2 Tune rules exceptions for known noise priority: NOTICE → CRITICAL custom rules for your apps 3 Route alerts stdout → sidecar → SIEM page only on CRITICAL noise kills the program Default rules are a starting point. Untuned Falco is a pager firehose. syscall → rules → alert → response
Observe — syscallsTune — rulesAlert — route
Check runtime agents
kubectl -n falco get pods
kubectl -n falco logs -l app.kubernetes.io/name=falco --tail=50

Interviewer often follows with: Give an example attack that passes PSA restricted but Falco should catch.

Architecture & troubleshooting
What are the control-plane components?Intermediate

kube-apiserver is the front door and the only component that talks to etcd. etcd stores cluster state. kube-scheduler places Pods. kube-controller-manager runs the reconcile loops. Nodes run kubelet and kube-proxy — or an eBPF dataplane.

clients / kubectlkube-apiserveretcdcontrollers + schedulerkubelet on nodes
Health of control plane (managed vs self-hosted)
kubectl get --raw='/readyz?verbose'
kubectl get componentstatuses  # legacy; prefer metrics / provider status
A Pod is CrashLoopBackOff — how do you triage?Intermediate

I'd describe for events and lastState, then logs --previous for the crashed process. From there I'm looking at exit code, probes, OOM — 137 — config or secret mounts, and the entrypoint. kubectl debug if I need a shell.

describe: events + lastStatelogs --previousexit code / OOM / configfix & redeploy

CrashLoop means the container exits and the kubelet backs off (10s → 20s → 40s …).

Triage commands
kubectl describe pod web
kubectl logs web --previous
kubectl debug web -it --image=busybox --target=app
ImagePullBackOff — what are the usual causes?Beginner

Wrong image name or tag, registry auth missing or expired, rate limits, or the node can't reach the registry. I'd fix the reference, imagePullSecrets, or network path — restarts alone won't help.

Read pull errors
kubectl describe pod web | grep -A15 Events
kubectl get event --field-selector reason=Failed
# check imagePullSecrets on the ServiceAccount / Pod
A Pod is stuck Pending — how do you triage it end to end?Advanced

I'd start with describe Events for FailedScheduling. Then I'm checking resource requests vs free capacity, taints and tolerations, affinity, PVC binding, and ResourceQuota. Pending is almost never "the app is broken" — it's placement.

I'd read the scheduler message carefully: Insufficient cpu/memory, node(s) had taint, persistentvolumeclaim not bound, didn't match Pod's node affinity, and so on. Quota can reject Pod creates before scheduling even starts. Unbound PVCs with WaitForFirstConsumer wait for a feasible node; Immediate binding in the wrong zone can leave you Pending forever. If CA is enabled, I'd confirm it sees the unschedulable Pod and that the node group can satisfy the constraints — GPU, zone, taints.

Pending means the scheduler could not place it

FailedScheduling events tell you which filter failed — start there.

Pod Phase: Pending kube-scheduler FailedScheduling event No node fit / filter fail 1 describe first kubectl describe pod Events: FailedScheduling the reason string is the answer 2 Common causes requests · taints · affinity PVC Pending · ResourceQuota nodeSelector mismatch 3 Fix the cause not the Pod object free capacity / add toleration bind PVC / raise quota Pending means the scheduler rejected every node. Read Events before guessing. Pod → scheduler filters → FailedScheduling → fix cause
describe — EventsCauses — fit failFix — root cause
Pending triage
kubectl describe pod web | grep -A30 Events
kubectl get pods -o wide
kubectl describe pvc data
kubectl get nodes -o custom-columns=NAME:.metadata.name,CPU:.status.allocatable.cpu,MEM:.status.allocatable.memory

Interviewer often follows with: How do you tell ResourceQuota rejection from FailedScheduling?

Walk through what happens on kubectl apply -f app.yaml.Advanced

kubectl sends the object to the apiserver, which authenticates, authorizes via RBAC, runs admission, validates, and persists to etcd. Controllers reconcile desired state; the scheduler binds Pods; kubelets start containers.

Client-side apply stores a last-applied annotation and three-way merges; server-side apply uses field managers and is what I'd prefer for multi-actor control. I'd diff before apply in CI. After persist, nothing "runs" inside the apiserver — controllers watch and act. A successful apply that creates a Deployment can still leave Pods Pending or CrashLooping, so I'd always follow with rollout status and Pod events.

Request path through the control plane

Only the apiserver talks to etcd; controllers and the scheduler watch the API.

etcd cluster state store 3 kubectl / clients apply · get · watch 1 kube-apiserver authn authz admission 2 controller-manager reconcile loops 4 kube-scheduler assign to node 5 kubelet node agent 6 container runtime containerd · CRI 7 pods workloads 8 kube-proxy service routing external users ingress traffic CONTROL PLANE · REQUEST LIFECYCLE DATA PLANE · SERVICE TRAFFIC
API requestetcd writeetcd readrun workloaduser traffic
kubectl applyauthn / authzadmissionetcdcontrollersschedulerkubelet
Preview then apply
kubectl diff -f app.yaml
kubectl apply -f app.yaml --server-side
kubectl rollout status deploy/web

Interviewer often follows with: What is the difference between client-side and server-side apply?

Why does etcd matter, and how do you protect it?Expert

etcd is the source of truth for cluster state — lose it and you lose the control plane's memory. I'd protect it with TLS and peer auth, encryption at rest for Secrets, network isolation, and tested snapshots.

Run an odd number of members — 3 or 5 — for Raft quorum; even counts hurt fault tolerance. etcd is latency-sensitive — local SSD, low peer RTT. Restrict network access so only apiservers speak to it. Snapshot regularly and practice restore on a non-prod cluster; an untested backup isn't a backup. Managed Kubernetes hides etcd, but you still own encryption-at-rest settings and the provider's control-plane backup posture.

Snapshot and verify (self-managed)
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
  --cacert=ca.crt --cert=peer.crt --key=peer.key snapshot save snap.db
etcdctl snapshot status snap.db -w table

Interviewer often follows with: What happens to quorum if you lose two members of a five-node etcd cluster?

A node goes NotReady — what happens to Pods, and how do you investigate?Expert

The node stops heartbeating; after grace periods it's NotReady and controller-owned Pods get evicted and rescheduled — bare Pods don't. I'd check kubelet, runtime, disk or memory pressure, and connectivity to the apiserver.

node-monitor-grace-period marks NotReady; pods go NotReady quickly, but mass eviction waits for the eviction timeout so brief blips don't thrash the cluster. DaemonSet pods stay tied to the node. Common causes I've seen: kubelet crash, container runtime dead, disk pressure, CNI failure, or a lost route to the API. I'd cordon before maintenance so nothing new lands while I drain with PDB awareness.

kubelet stops heartbeatingNode → NotReadyeviction timeoutPods rescheduled
Investigate the node
kubectl describe node n1 | grep -A8 Conditions
kubectl get pods -A -o wide --field-selector spec.nodeName=n1
kubectl cordon n1
kubectl drain n1 --ignore-daemonsets --delete-emptydir-data

Interviewer often follows with: Why might a drain hang even though the node is NotReady?

How do you design for blast-radius control across namespaces and nodes?Expert

I'd combine namespace quotas, PSA, NetworkPolicy, dedicated node pools with taints for noisy or sensitive workloads, and Pod anti-affinity or topology spread so one zone or node failure can't take the whole service down.

Blast radius is an architecture interview favorite. Soft tenancy limits who can break whom via API and network; topology spread and anti-affinity limit correlated failure; separate system, ingress, and tenant node pools keep a rogue DaemonSet from starving core addons. PDBs only protect voluntary disruption. I'd measure with failure injection — cordon a zone, break DNS NetworkPolicy, revoke a deploy SA — and confirm blast radius matches the design.

Spread + taint pattern
topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: topology.kubernetes.io/zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector: { matchLabels: { app: web } }
kubectl taint nodes -l pool=ingress dedicated=ingress:NoSchedule

Interviewer often follows with: What still correlates if every replica lands in one AZ despite a Deployment?

Real-world scenarios
Production API pods went CrashLoopBackOff right after a secrets rotation. The Deployment and image did not change. How do you triage?Advanced

I'd bet the new Secret materialized wrong — missing key, wrong name, or bad mount path. I'd describe lastState and exit, pull logs --previous for "file not found" or auth errors, then diff the Secret keys against what the container expects. Fix the Secret or volume mapping and let the kubelet remount — I wouldn't restart blindly.

Last time I saw this, ExternalSecrets had written a partial Secret after a Vault blip. Other common paths: key rename in the provider while volume items still reference the old key; envFrom or secretKeyRef pointing at a key that no longer exists so the container exits immediately; optional: false on a missing key failing the Pod before start. Base64 in the Secret is encoding only — a "rotated" value that looks fine in kubectl get secret -o yaml can still be wrong plaintext. I'd also check projected vs classic mounts, immutable Secrets that block updates, and whether the app caches credentials at start so a remount alone isn't enough without a rollout.

Synced Secret → mount → app start

Rotation fails when the key name or mount path drifts, not when base64 "looks different."

Vault / AWS external store External Secrets Operator SecretStore + ExternalSecret K8s Secret → Pod mount 1 SecretStore how to reach the backend auth: IRSA / Vault SA / keys cluster- or namespace-scoped 2 ExternalSecret which keys + refreshInterval maps remote → Secret data controller reconciles loop 3 Synced Secret native K8s Secret object mount / env as usual rotate at the store, not YAML ESO copies values into the cluster. The source of truth stays outside Kubernetes. store → SecretStore → ExternalSecret → Secret → Pod
Store — sourceESO — syncSecret — consume
Compare Secret keys to the mount
kubectl describe pod api-7f9c -c api | grep -A20 'Mounts\|Events\|Last State'
kubectl logs api-7f9c -c api --previous
kubectl get secret api-creds -o jsonpath='{.data}' | jq 'keys'
# Pod expects DB_PASSWORD; volume items must match
kubectl get pod api-7f9c -o jsonpath='{.spec.volumes}' | jq .

Interviewer often follows with: Would restarting the Pod fix a missing secretKeyRef, and why or why not?

Users see intermittent 503s after a deploy. Some pods are Ready, HPA is scaling, and endpoints flicker. What is your differential diagnosis?Expert

I'd stabilize readiness first — that's usually the smoking gun when endpoints flicker. Then I'd check whether HPA is thrashing short-lived Pods that never stay Ready, and whether EndpointSlice churn is overwhelming kube-proxy or conntrack. Freeze HPA max if I need breathing room.

What I've seen: readiness tied to a dependency so a DB blip flaps every Pod and you get mass 503s even though processes are alive; readiness timeout too tight vs cold start so new HPA pods never stay Ready, old pods overload, HPA scales more; maxUnavailable plus surge during rollout with aggressive probes leaving a temporary empty Ready set; EndpointSlice updates racing with LB health checks. Mitigations I'd reach for: shallow liveness, readiness that reflects the local accept loop, startupProbe, HPA stabilizationWindowSeconds and scale-down policies, and minReadySeconds. I wouldn't "fix" 503s by removing readiness — that just ships traffic to broken pods.

Readiness gates Service backends

Flapping readiness + HPA surge = endpoint churn and intermittent 503s.

kubelet runs probes on a schedule 1 Readiness can this pod take traffic? fail → drop from Endpoints no container restart deps / warm caches OK here 2 Liveness is the process deadlocked? fail → kubelet restarts it drops in-flight work never ping shared deps 3 Startup still booting? hold off delays readiness + liveness until success or timeout JVM / .NET slow boots Swap readiness and liveness and a DB blip becomes a fleet-wide restart. separate paths: /ready · /healthz · startupProbe window
Readiness — traffic gate Liveness — restart trigger Startup — boot shield
probe failPod not Readydropped from EndpointSliceLB 503 / empty pool
Correlate Ready vs endpoints vs HPA
kubectl get pods,endpointslices -l app=api -o wide
kubectl describe hpa api
kubectl get --raw /api/v1/namespaces/prod/endpointslices | jq '.items[]|select(.metadata.labels["kubernetes.io/service-name"]=="api")'
kubectl get events --field-selector reason=Unhealthy --sort-by=.lastTimestamp | tail -20

Interviewer often follows with: How would you tell readiness flapping from genuine upstream overload?

A StatefulSet Pod is Pending; the PVC is also Pending. Nodes have free CPU/memory. What do you check for zone / WaitForFirstConsumer issues?Advanced

I'd describe the PVC and Pod together. With WaitForFirstConsumer the volume waits for a feasible node; Immediate binding can pin a PV to the wrong zone so the Pod never schedules. I'd check StorageClass volumeBindingMode, topology, and zone affinity.

Classic trap I've hit: StorageClass uses Immediate, CSI provisions in zone-a, Pod anti-affinity or nodeSelector wants zone-b — forever Pending with a volume node affinity conflict. WaitForFirstConsumer delays binding until the scheduler picks a node, then provisions in-zone — that's what I'd prefer for zonal disks. I'd also check ResourceQuota on storage, wrong accessMode, a deleted PV still referenced, and topology constraints that leave no legal zone. Fix is usually recreate the PVC with the right SC, or carefully relax affinity after confirming data-loss risk.

PVC topology can block scheduling

WaitForFirstConsumer binds after a feasible node exists; Immediate can strand the volume.

Pod Phase: Pending kube-scheduler FailedScheduling event No node fit / filter fail 1 describe first kubectl describe pod Events: FailedScheduling the reason string is the answer 2 Common causes requests · taints · affinity PVC Pending · ResourceQuota nodeSelector mismatch 3 Fix the cause not the Pod object free capacity / add toleration bind PVC / raise quota Pending means the scheduler rejected every node. Read Events before guessing. Pod → scheduler filters → FailedScheduling → fix cause
describe — EventsCauses — fit failFix — root cause
PVC + schedule events
kubectl describe pvc data-db-0
kubectl get sc -o custom-columns=NAME:.metadata.name,BINDING:.volumeBindingMode,PROVISIONER:.provisioner
kubectl describe pod db-0 | grep -A25 Events
kubectl get pv -o wide

Interviewer often follows with: When is Immediate binding still acceptable?

Pods suddenly cannot resolve cluster DNS names. kubectl works. Walk through ndots, CoreDNS, and NetworkPolicy as causes.Expert

From a debug pod I'd nslookup kubernetes.default.svc.cluster.local and kube-dns. Check CoreDNS pods and logs, then NetworkPolicies that deny egress to kube-system:53. ndots:5 causing search-list amplification is a separate story from a total DNS outage.

Three failure classes I've chased: CoreDNS CrashLoop or Pending — cluster-wide timeouts; namespace NetworkPolicy default-deny egress without allowing UDP/TCP 53 to kube-dns — looks like "app DNS broken" while the API still works from my laptop; ndots:5 plus a long search path so short names generate many queries and under conntrack or CoreDNS saturation you see intermittent failures. Also CNI path to the CoreDNS ClusterIP, and NodeLocal DNSCache misconfig. Fix order for me: restore CoreDNS Ready, open DNS egress, then tune dnsConfig ndots for chatty clients.

Egress deny without DNS allow

API access ≠ Pod DNS. Policies that block kube-dns break name resolution in-namespace.

namespace: prod policy: default-deny ingress: from role=web egress :53 web pod role=web · matched reporting pod role=reporting · no rule public internet 0.0.0.0/0 · external CoreDNS kube-dns · UDP/TCP 53 payments app=payments policy target · deny-all + allow A pod is unrestricted until a policy selects it — then it is deny-all except the explicit allows.
Allowed — web ingress reaches payments Blocked — stopped at the default-deny boundary DNS — egress to CoreDNS on :53
DNS triage from inside the cluster
kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50
kubectl run -it --rm dbg --image=busybox:1.36 --restart=Never -- nslookup kubernetes.default.svc.cluster.local
kubectl get netpol -A
# resolv.conf: ndots and search
kubectl exec api-0 -- cat /etc/resolv.conf

Interviewer often follows with: What does ndots:5 change about how short names resolve?

Browser shows certificate expired on your Ingress hostname, but the TLS Secret was renewed an hour ago. Why might clients still see the old cert?Advanced

Ingress controllers often load TLS Secrets into memory and only reload on watch events — or they cache by secret name. I'd confirm the Secret data actually changed, that the Ingress references the right secretName, and force a controller reload if the watch missed the update.

Causes I've hit: cert-manager renewed a different Secret than the Ingress tls[].secretName; Secret updated but controller cache went stale — bounce the ingress controller pods; multiple Ingresses or Gateway listeners sharing a hostname with conflicting certs; a CDN or external LB terminating TLS with its own expired cert in front of the cluster; HTTP→HTTPS redirect to a different host. Kubernetes doesn't hot-reload TLS into every proxy by itself — the ingress implementation does. I'd verify with openssl s_client against the LB IP and against a port-forward to an ingress pod to split edge vs controller.

Secret → Ingress controller → client TLS

Renewing the Secret is necessary but not sufficient if the controller or edge cache still serves the old cert.

North-south traffic path from external user through Ingress, Service and EndpointSlice to Pods An external user sends an HTTPS request to the Ingress controller which terminates TLS, then routes in-cluster to a ClusterIP Service, an EndpointSlice of live pod IPs, and the backing Pods. HTTPS · encrypted route host / path select load-balance External User GET :443 client Ingress TLS terminated ingress controller single HTTPS front door Service ClusterIP · vIP stable virtual IP never changes Endpoint Slice live pod IPs real · changing Pods app backends
HTTPS (TLS) In-cluster routing Backend pods
Verify Secret vs what the edge serves
kubectl get ingress api -o yaml | grep -A6 tls
kubectl get secret api-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates -subject
kubectl -n ingress-nginx rollout restart deploy/ingress-nginx-controller
openssl s_client -connect api.example.com:443 -servername api.example.com </dev/null 2>/dev/null | openssl x509 -noout -dates

Interviewer often follows with: How do you prove the problem is the CDN versus the Ingress controller?

API server latency spiked; controllers and operators are slow; you suspect etcd and watch storms. How do you reason and respond?Expert

I'd treat apiserver/etcd latency as a cluster-wide incident. Check apiserver and etcd metrics — WAL fsync, request duration — find chatty clients and large LIST/WATCH callers, rate-limit or scale down noisy operators, and stop kubectl get -A loops. Stabilize etcd disk before tuning apps.

Watch storms happen when many controllers re-list and re-watch huge objects after timeouts, amplifying load when etcd is already slow — a positive feedback loop. Causes: saturated etcd disk, too many CRDs or objects, inefficient operators without pagination, admission webhooks adding latency. Response for me: protect etcd, shed load by disabling non-critical operators and stopping CI apply storms, fix hot webhooks, then tune. On managed control planes I'd open a provider ticket with metrics but still reduce client QPS from our side. I wouldn't "fix" by deleting etcd data.

Clients → apiserver → etcd

Slow etcd makes watches time out; clients retry and amplify the storm.

etcd cluster state store 3 kubectl / clients apply · get · watch 1 kube-apiserver authn authz admission 2 controller-manager reconcile loops 4 kube-scheduler assign to node 5 kubelet node agent 6 container runtime containerd · CRI 7 pods workloads 8 kube-proxy service routing external users ingress traffic CONTROL PLANE · REQUEST LIFECYCLE DATA PLANE · SERVICE TRAFFIC
API requestetcd writeetcd readrun workloaduser traffic
etcd slowwatch timeoutsclient re-listapiserver load ↑worse latency
Inspect control-plane health and noisy clients
kubectl get --raw='/readyz?verbose'
kubectl get --raw='/metrics' | grep -E 'apiserver_request_duration|etcd_request'
# self-managed: etcd fsync / disk
# find chatty controllers
kubectl get events -A --sort-by=.lastTimestamp | tail -30
kubectl top pods -A | sort -k3 -n | tail

Interviewer often follows with: Why can deleting a stuck operator deployment improve apiserver latency immediately?

A developer RoleBinding lets them get Secrets in prod they should not see. How do you audit exposure and fix it without breaking their workflow?Advanced

I'd impersonate or use auth can-i to map effective permissions, find the Role or ClusterRole rules granting secrets get/list/watch, remove or replace with least privilege, rotate anything they could have read, and review audit logs for prior access.

Audit path: kubectl auth can-i --list --as=user, check RoleBindings and ClusterRoleBindings, watch for aggregation labels and wildcards. secrets get isn't mitigated by base64 — that isn't encryption. Fix: replace with get on ConfigMaps only, or use External Secrets patterns so humans never need kubectl get secret. Rotate credentials that were readable. Longer term: admission policy blocking Role rules on secrets, periodic access reviews, and namespace separation of prod. Encryption at rest doesn't help against authorized API reads.

Subject → RoleBinding → Role verbs

Effective access is the union of all bindings — wildcards are the usual footgun.

authenticated request allow deny kubelet TokenRequest projection issues + rotates the token Pod workload container SA token · JWT short-lived · audience-bound kube-apiserver request pipeline · two gates 1 · Authenticate verify signature + expiry → system:serviceaccount:ns:name 2 · Authorize · RBAC check (Cluster)RoleBinding for that ServiceAccount ✓ etcd / resource ALLOWED · request proceeds ✕ 403 Forbidden DENIED · no binding grants it Identity = the signed token · Permissions = RBAC. A leaked long-lived token is dangerous, so tokens are short-lived + audience-scoped.
authenticated request allowed denied token issued / rotated
Map and revoke secrets access
kubectl auth can-i get secrets -n prod [email protected]
kubectl auth can-i --list -n prod [email protected] | grep -i secret
kubectl get rolebinding,clusterrolebinding -A -o wide | grep -i dev
kubectl edit role dev-edit -n prod   # remove secrets from rules
# rotate after exposure
kubectl delete secret db-creds -n prod  # then recreate from source of truth

Interviewer often follows with: Does enabling etcd encryption at rest fix this RBAC mistake?

Your canary is erroring but 10% of traffic still hits it. Leadership wants rollback now. What do you do under pressure?Advanced

I'd shift weight back to stable immediately via Gateway, Ingress, or mesh config — I wouldn't wait for an image rebuild. Keep the canary Deployment around for forensics, or scale it to zero after traffic is off. Confirm metrics go green, then post-mortem.

Priority is traffic, not deleting pods. If we're on Gateway API HTTPRoute weights, I'd patch canary to 0 and stable to 100. Flagger or Argo rollouts: abort or undo. Mesh: restore destination rule subsets. Pitfalls: DNS or CDN caching of canary routes, sticky sessions still pinning users, and a canary that shared a Deployment with stable so rollout undo is the right tool instead of weights. PDB doesn't stop you from changing route weights. After cutback, capture logs from canary pods before scale-to-zero. Separate "stop the bleeding" from "root cause."

Canary weight cutback under pressure

Move traffic first; analyze the bad revision after users are safe.

Service routes to Ready pods only Deployment web · rolling update owns 2 ReplicaSets maxSurge = 1 (one extra pod) maxUnavailable = 0 (never dip) ReplicaSet v1 old · scaling 3 → 1 ReplicaSet v2 new · scaling 0 → 2 readiness gate 2 terminating · 1 Ready 1 starting · 1 Ready broken v2 fails readiness → rollout pauses · v1 keeps serving
live traffic → Ready pods only scaling up (new pods starting) draining (old pods terminating)
Zero canary weight (Gateway API sketch)
kubectl patch httproute api -n prod --type=json -p='[
  {"op":"replace","path":"/spec/rules/0/backendRefs/0/weight","value":100},
  {"op":"replace","path":"/spec/rules/0/backendRefs/1/weight","value":0}
]'
kubectl get pods -l app=api,track=canary -o wide
kubectl logs -l app=api,track=canary --tail=100

Interviewer often follows with: When is rollout undo the wrong rollback tool for a canary?

Nodes show DiskPressure; kubelet is evicting pods including ones you consider critical. How do you respond and harden?Expert

I'd relieve disk immediately — clear image and container garbage, fix log fill-up, cordon the worst nodes. PriorityClass and requests don't stop eviction when disk thresholds trip. Free space, fix the writers, then tune eviction thresholds and PriorityClass for system-critical addons only.

DiskPressure eviction is involuntary from the PDB's point of view — PDBs don't protect against kubelet eviction for disk. Rank for me: free node filesystem under /var/lib/containerd and logs, find pods writing to emptyDir without limits, check ephemeral storage requests and limits. QoS Guaranteed helps memory and CPU contention more than disk — ephemeral storage limits are the right lever. Critical DaemonSets should use high PriorityClass and careful node sizing. Root causes I've seen: unbounded logs, image pull storms, leftover failed pods. Longer term: log rotation, GC settings, separate disk for the runtime, alerts on nodefs/imagefs before the eviction threshold.

nodefs/imagefs lowDiskPressure=Truekubelet evictsPods reschedulemay refill disk
Confirm pressure and evictions
kubectl describe node n3 | grep -A12 Conditions
kubectl get pods -A -o wide --field-selector spec.nodeName=n3
kubectl get events -A --field-selector reason=Evicted --sort-by=.lastTimestamp | tail -20
# on the node (via provider console / ssh / debug): df -h /var/lib/containerd
kubectl cordon n3

Interviewer often follows with: Why will a PDB not save your API pods from DiskPressure eviction?

A Job shows Active pods and Running, but Completions never reach the target. How do you debug it?Advanced

I'd inspect Job conditions and Pod exit codes. Completions only count successful pods. Failures may retry until backoffLimit, hang without exiting, or leave pods Running because the process never terminates. Fix the workload or set activeDeadlineSeconds and backoffLimit.

Patterns I've seen: container sleeps forever after work so the Job never sees Succeeded; crash loop consuming backoffLimit so the Job Failed; parallelism greater than 1 with Indexed jobs and some indexes stuck; a sidecar keeping the Pod Running after the main container exits. I'd check .status.succeeded vs .spec.completions, and ttlSecondsAfterFinished for cleanup. I wouldn't delete the Job blindly if I need logs — fetch logs first. For CronJobs I'd also check concurrencyPolicy and startingDeadlineSeconds for overlapping runs.

Job status vs pod exits
kubectl describe job migrate
kubectl get pods -l job-name=migrate -o wide
kubectl get pods -l job-name=migrate -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.status.phase}{" "}{.status.containerStatuses[0].state}{"\n"}{end}'
kubectl logs job/migrate --all-containers
kubectl get job migrate -o yaml | grep -A20 'status:\|backoffLimit\|completions'

Interviewer often follows with: How do sidecars prevent a Job from reaching Completions?

App container is Ready and healthy, but the Service still fails for clients. A mesh/agent sidecar is CrashLooping. What is going on?Expert

Traffic often enters via the sidecar proxy. If the proxy is down, endpoints may still list the Pod IP while the data-plane redirect black-holes traffic — or readiness only checked the app container. I'd fix injection or proxy config, and make readiness reflect the proxy.

Classic mesh failure: iptables or eBPF redirects pod traffic to an Envoy or linkerd-proxy that isn't listening — app /healthz on the app port still passes if probes bypass the proxy or target the app container. I'd make sure the proxy has its own readiness probe. Also holdApplicationUntilProxyStarts or native sidecars so the app doesn't accept before the proxy is up. Non-mesh agents usually don't steal the Service path unless they share the network incorrectly — I'd focus on whether the CNI or mesh hijacks inbound. STRICT mTLS with missing sidecar peers drops traffic hard.

Client → sidecar proxy → app

App Ready ≠ proxy Ready when the mesh owns the data path.

POD A POD B Mesh CA / SPIFFE issues short-lived SVID certs app unaware sidecar proxy Envoy · mTLS sidecar proxy Envoy · mTLS app unaware mTLS encrypted both present + verify certs SVID SVID plaintext plaintext
plaintext app ↔ sidecar (in-pod) encrypted mTLS tunnel between sidecars cert issuance SPIFFE / mesh CA
Per-container Ready and proxy logs
kubectl get pod api-1 -o jsonpath='{range .status.containerStatuses[*]}{.name}{" ready="}{.ready}{"\n"}{end}'
kubectl logs api-1 -c istio-proxy --previous
kubectl describe pod api-1 | grep -A30 'Readiness\|Events'
kubectl get endpointslices -l kubernetes.io/service-name=api -o yaml

Interviewer often follows with: Should Service readiness consider only the app container in a meshed Pod?

Cluster upgrade drain is stuck; nodes will not empty. You suspect PodDisruptionBudgets. How do you unblock safely?Advanced

I'd find pods blocking voluntary eviction with describe pdb and eviction API errors. Raise replicas or temporarily relax maxUnavailable/minAvailable so the PDB is satisfiable, drain one node at a time, then restore the PDB. Force delete bypasses PDB but risks outage — that's break-glass.

Drain uses the Eviction API and must respect PDBs. Deadlock patterns: minAvailable equals replica count, multiple PDBs selecting the same pods, or HPA minReplicas too low so you can't surge healthy pods before drain. PDB doesn't block node crashes or kubelet eviction. Safe unblock for me: scale the Deployment up, wait Ready, drain; or patch PDB maxUnavailable during a change window with an explicit revert. kubectl drain --force --disable-eviction is last resort. I'd also check terminating pods waiting on finalizers and StatefulSet OrderedReady stuck on an unhealthy ordinal.

Find which PDB blocks the drain
kubectl get pdb -A
kubectl describe pdb api -n prod
kubectl drain n5 --ignore-daemonsets --delete-emptydir-data --dry-run=server
# if AllowedDisruptions is 0, scale out first
kubectl scale deploy/api --replicas=4
kubectl drain n5 --ignore-daemonsets --delete-emptydir-data

Interviewer often follows with: What is the difference between a PDB blocking drain and a finalizer stuck Terminating?

Finance flags a 4× overnight compute bill. HPA maxed replicas and Cluster Autoscaler added dozens of nodes. How do you stop the bleeding and prevent recurrence?Expert

I'd immediately lower HPA maxReplicas and/or pause CA scale-up — set a node-group max. Find the metric that drove scale — error loop, bad custom metric, CPU spin. Fix the app or metric, then put hard caps, scale-down stabilization, and budget alerts in place.

Common drivers I've seen: readiness or liveness misconfig causing restart CPU spikes that HPA reads as load; a custom metric that counts errors or queue depth without a ceiling; retry storms amplifying work as replicas grow; CA with unbounded max nodes. HPA and CA reinforce each other — more pods → Pending → more nodes → more scheduled pods. Mitigations: maxReplicas, CA utilization thresholds and max size, separate node pools with caps, scale-down-delay, and alerts on replica count and node count. Cost control is capacity design, not only rightsizing requests.

HPA replicas ↔ CA nodes feedback

Unbounded maxReplicas + unbounded node groups can burn budget overnight.

Metrics Server CPU / memory HPA controller targetUtilization × requests Deployment replicas N → M ! No requests = stuck utilization = usage / request missing request → 0% HPA sits at minReplicas 1 Scale the Deployment never scale Pods directly min / max + behavior windows stabilization avoids flapping 2 Verify kubectl top + HPA TARGETS load test until scale-up alert when pinned at max HPA reads metrics and patches Deployment.spec.replicas. Requests make utilization real. metrics → HPA → Deployment replicas → Pods
Metrics — serverHPA — decideScale — Deployment
metric spikeHPA adds podsPendingCA adds nodesmore capacity → more scale
Cap scale and inspect why
kubectl patch hpa api --type=merge -p '{"spec":{"maxReplicas":10}}'
kubectl describe hpa api
kubectl get nodes -o wide | wc -l
# CA logs (kube-system) for scale-up decisions
kubectl -n kube-system logs -l app=cluster-autoscaler --tail=100 | grep -i scale

Interviewer often follows with: Why can fixing CPU requests alone fail to stop this loop?

Design interview: how would you fail over a stateless API across two Kubernetes clusters in different regions?Expert

I'd run active-active or warm-standby with independent control planes, replicate config via GitOps, keep data-layer failover separate, and shift traffic at DNS or a global LB based on health. Practice failback. Avoid split-brain on stateful dependencies.

Stateless pods are the easy part; state is the interview. Patterns: active-passive with health-checked Global Accelerator or DNS failover; active-active with locality and conflict-free data, or regional DBs with async replica and an accepted RPO. GitOps promotes the same revision to both clusters. Secrets and Ingress TLS must exist in both. Health signals should check regional dependencies, not only Pod Ready. Active-active doubles cost and needs careful session affinity; active-passive is cheaper but higher RTO. PDBs and HPA are per-cluster — they don't span clusters. I'd test by withdrawing a region's pool from the global LB, not by hoping etcd somehow syncs across regions.

global LB healthregion A / region B clustersGitOps same revisiondata tier strategy
Per-cluster health for global LB
# each cluster exposes /readyz behind its Ingress
kubectl --context=prod-east get deploy,api,ing -n api
kubectl --context=prod-west get deploy,api,ing -n api
# traffic shift is DNS/GLB weight — not kubectl in one cluster
# verify both revisions match
kubectl --context=prod-east get deploy api -o jsonpath='{.spec.template.spec.containers[0].image}'
kubectl --context=prod-west get deploy api -o jsonpath='{.spec.template.spec.containers[0].image}'

Interviewer often follows with: What breaks if you only replicate Kubernetes YAML but not the database failover plan?

Pod is OOMKilled (exit 137). memory limit equals request (Guaranteed QoS), yet RSS spiked under load. What do you explain and change?Advanced

Guaranteed QoS doesn't prevent OOM when usage exceeds the limit — the cgroup still kills on limit breach. I'd raise limit and usually request together, fix the leak or spike, or carefully use memory-aware HPA/VPA. limit equals request only means QoS class, not infinite headroom.

Myths I'd kill in the interview: Guaranteed doesn't mean immune to OOM; request is scheduling reservation, limit is the cgroup hard cap. RSS plus page cache plus JVM non-heap can exceed what people expect from "heap at 80% of limit." If limit equals request and the spike is real working set, you have to raise both or reduce footprint. I'd check events for Last State OOMKilled, container_memory_* metrics, and remember that noisy neighbors don't cause a limit breach — that's local cgroup. Temporary mitigation: raise limit, add memory load testing, profile the spike.

Confirm OOM and current requests/limits
kubectl describe pod api-3 | grep -A15 'Last State\|Limits\|Requests\|OOM'
kubectl get pod api-3 -o jsonpath='{.spec.containers[0].resources}' ; echo
kubectl top pod api-3 --containers
# events
kubectl get events --field-selector involvedObject.name=api-3

Interviewer often follows with: If limit equals request, are you protected from node-level memory pressure eviction?

Image pulls succeed on some nodes but fail with ImagePullBackOff on others for the same private image. How do you triage?Expert

I'd compare node identity and pull credentials — imagePullSecrets on the ServiceAccount or Pod versus node instance IAM, IRSA, or workload identity for the registry. Nodes in a different subnet or without the registry route will fail even when the Secret is correct.

Split-brain pull auth I've chased: Pod-level imagePullSecrets work everywhere the Secret is mounted — if only some nodes fail, look at node networking and endpoint reachability; EKS/GKE-style node IAM or kubelet credential providers — only nodes with the right role succeed, and new node groups often miss the policy; different runtimes or mirrors per pool; rate limits keyed by egress IP with one poisoned NAT gateway. Fix: align node roles, attach imagePullSecrets to the SA for consistency, verify registry firewall per subnet. describe Events show "unauthorized" vs "timeout" — I'd treat those differently.

Contrast a good node vs a bad node
kubectl describe pod web-a | grep -A20 Events
kubectl get pod web-a web-b -o wide
kubectl get sa default -o yaml | grep -A5 imagePullSecrets
kubectl get nodes n-good n-bad -o yaml | grep -E 'node.kubernetes.io|iam|instance'
# from each node path: can the registry be reached?
kubectl debug node/n-bad -it --image=busybox -- wget -S -O- https://registry.example.com/v2/

Interviewer often follows with: When would fixing the ServiceAccount imagePullSecrets not help the failing nodes?

Go deeper
Hands-on courses for Kubernetes