The 60 Kubernetes questions interviewers actually ask, with direct answers, runnable manifests and kubectl, and what the interviewer is listening for. Grouped for freshers, intermediate, and experienced rounds.
60 questions with answersKey Takeaways
Kubernetes, often shortened to K8s, is an open-source container orchestration system first released by Google in 2014 and now maintained by the Cloud Native Computing Foundation. It groups containers into logical units, schedules them onto a pool of machines, restarts failed ones, scales them up and down, and gives them stable networking and storage. The core idea is declarative: you describe the desired state in YAML, and Kubernetes runs a control loop that keeps reconciling the real cluster toward that description. According to the official Kubernetes documentation, the platform provides a framework to run distributed systems resiliently, handling scaling, failover, and deployment patterns for you. In interviews, Kubernetes questions probe whether you understand this reconciliation model and the core objects (Pods, Deployments, Services, ConfigMaps) rather than memorized flags. This page collects the 60 questions that come up most, each with a direct answer and runnable manifests. If the first round runs as a live AI coding interview, pair this bank with our AI interview preparation guides for the format side.
Watch: Kubernetes Tutorial for Beginners [FULL COURSE in 4 Hours]
Video: Kubernetes Tutorial for Beginners [FULL COURSE in 4 Hours] (TechWorld with Nana, YouTube)
Test yourself and earn a certificate
10 quick questions. Score 70%+ to download your Kubernetes certificate.
The fundamentals every entry-level round checks. If any answer here surprises you, that's your study list.
Kubernetes is an open-source system that automates deploying, scaling, and running containerized applications across a cluster of machines. You describe the state you want in YAML, and Kubernetes keeps the cluster matching it.
It solves the problem of running containers at scale: scheduling them onto machines, restarting the ones that die, scaling them up under load, rolling out new versions without downtime, and giving them stable networking. Doing all that by hand across many servers is what Kubernetes takes off your plate.
Key point: A one-line definition plus the words self-healing, scaling, and rolling updates beats a memorized feature list. Interviewers open with this to hear how you frame an answer.
Watch a deeper explanation
Video: Kubernetes Explained in 100 Seconds (Fireship, YouTube)
A Pod is the smallest deployable unit in Kubernetes. It wraps one or more containers that share the same network namespace (one IP, one localhost) and can share storage volumes. Containers in a Pod always run together on the same node.
Most Pods hold a single container. Multiple containers in one Pod is the sidecar pattern, where a helper (a log shipper, a proxy) runs alongside the main app and needs to share its network or files.
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80Key point: The follow-up is almost always 'when would you put two containers in one Pod?'. Have the sidecar example ready.
A container is a packaged app process. A Pod is the Kubernetes wrapper around one or more containers that share network and storage. A node is a worker machine (virtual or physical) that runs Pods.
So the nesting is: containers live inside Pods, Pods run on nodes, and nodes form a cluster. Kubernetes schedules Pods onto nodes; you rarely think about individual containers directly.
| Term | What it is | You manage it via |
|---|---|---|
| Container | A running app image | Indirectly, through a Pod spec |
| Pod | One or more containers sharing network and storage | Deployments, StatefulSets, and other controllers |
| Node | A worker machine running Pods | The control plane and the kubelet |
The control plane is the brain of the cluster: it makes global decisions like scheduling and responds to events. Worker nodes run the actual application Pods. Together they form a cluster.
Control plane components include the API server (the front door), etcd (the datastore), the scheduler (places Pods), and controllers (reconcile state). Each worker node runs the kubelet (starts Pods), a container runtime, and kube-proxy (networking).
Key point: Being able to The four control plane pieces and the three node pieces is the bar for this question. Say what each does in a few words.
Watch a deeper explanation
Video: Kubernetes Explained in 6 Minutes | k8s Architecture (ByteByteGo, YouTube)
A Deployment manages a set of identical Pods for you: it keeps the desired number running, replaces ones that die, and rolls out new versions gradually with the option to roll back. It does this by managing ReplicaSets under the hood.
A bare Pod is not self-healing: if it dies or its node fails, nothing recreates it. A Deployment is the object you use for almost every stateless app because it gives you self-healing and rolling updates for free.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27Key point: The key phrase is self-healing plus rolling updates. a Deployment owns a ReplicaSet.
A Service gives a group of Pods a single stable IP and DNS name, and load-balances traffic across them. Pods are ephemeral: they get new IPs when recreated, so you can't rely on a Pod IP. The Service is the stable address in front of them.
A Service finds its Pods with a label selector, so as Pods come and go the Service automatically tracks the current healthy set. Callers just talk to the Service name.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80Key point: The core insight is that Pod IPs are unstable, so you need a stable front. If you say that, the rest of the answer follows.
ClusterIP (the default) exposes the Service only inside the cluster. NodePort opens a fixed port on every node so traffic can reach the Service from outside. LoadBalancer provisions an external load balancer from the cloud provider and routes to the Service.
There's also ExternalName, which maps a Service to an external DNS name. In practice you use ClusterIP for internal traffic and a LoadBalancer or an Ingress for external traffic.
| Type | Reachable from | Typical use |
|---|---|---|
| ClusterIP | Inside the cluster only | Internal service-to-service traffic |
| NodePort | Any node IP on a fixed port | Simple external access, dev, or behind an LB |
| LoadBalancer | External, via a cloud load balancer | Public services on a cloud provider |
| ExternalName | Maps to an external DNS name | Aliasing an outside service |
A namespace is a virtual partition inside a cluster that scopes names and lets you group resources. Two Pods can share a name if they're in different namespaces. It's how you separate teams, environments, or apps in one cluster.
Namespaces are also where you attach resource quotas and access control. Cluster-wide objects like nodes and PersistentVolumes are not namespaced; most everyday objects like Pods, Services, and Deployments are.
kubectl create namespace staging
kubectl get pods -n staging
kubectl get pods --all-namespaceskubectl is the command-line client for Kubernetes. Every command it runs becomes an HTTP request to the cluster's API server, which is the single entry point for reading and changing cluster state.
Your connection details (server URL, credentials, current context) live in a kubeconfig file, usually at ~/.kube/config. That's why kubectl can switch between clusters: it just points at a different context.
kubectl get pods
kubectl describe pod web
kubectl apply -f deployment.yaml
kubectl config current-contextImperative commands tell Kubernetes to do a specific action now: kubectl create, kubectl run, kubectl delete. Declarative management describes the end state in YAML and lets kubectl apply figure out the changes needed to reach it.
Declarative is the production practice: the YAML lives in version control, apply is idempotent, and the files are the source of truth. Imperative commands are handy for quick experiments and debugging.
# imperative: do this now
kubectl run web --image=nginx
# declarative: make the cluster match this file
kubectl apply -f web.yamlKey point: Saying that declarative plus version control is how real teams work signals you've seen production, not just tutorials.
A ReplicaSet keeps a specified number of identical Pods running. If a Pod dies, the ReplicaSet creates a replacement; if there are too many, it deletes extras. It's the object that actually delivers self-healing for a fixed count.
You rarely create ReplicaSets directly. A Deployment creates and manages them, spinning up a new ReplicaSet during a rolling update while scaling down the old one.
Labels are key-value tags you attach to objects, like app: web or env: prod. Selectors are queries that match objects by their labels. This is the loose coupling glue of Kubernetes: Services, Deployments, and network policies find their targets by selector, not by name.
Because matching is by label, you can add or remove Pods from a Service just by changing labels, with no reference to individual Pod names or IPs.
kubectl label pod web tier=frontend
kubectl get pods -l app=web,env=prod
kubectl get pods -l 'env in (prod, staging)'Both inject configuration into Pods so you don't bake config into images. A ConfigMap holds non-sensitive data like URLs and feature flags. A Secret holds sensitive data like passwords and tokens, and its values are base64 encoded.
Base64 is encoding, not encryption, so Secrets aren't secure by default. In production you enable encryption at rest for etcd and control access with RBAC. Both can be mounted as files or exposed as environment variables.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: info
API_URL: https://api.example.comKey point: The gotcha interviewers wait for is 'are Secrets encrypted?'. The correct answer is no, base64 is just encoding; you add encryption at rest and RBAC.
A VM virtualizes hardware and runs a full guest operating system, so it's heavier and slower to start. A container shares the host kernel and isolates just the process, so it's lightweight, starts in seconds, and packs more densely.
Kubernetes orchestrates containers, not VMs. This matters in interviews because it explains why containers are the unit: they're cheap to create and destroy, which is exactly what self-healing and autoscaling need.
The kubelet is the agent that runs on every worker node. It watches the API server for Pods assigned to its node, tells the container runtime to start them, and reports their status back. It also runs the liveness and readiness probes.
Think of it as the node's hands: the scheduler decides where a Pod goes, but the kubelet is what actually makes the containers run on that machine and keeps them alive.
The scheduler watches for Pods with no assigned node. For each one it filters nodes that can run the Pod (enough CPU and memory, matching node selectors, tolerating taints), then scores the survivors and picks the best. It writes the chosen node onto the Pod, and the kubelet there takes over.
You influence placement with resource requests, nodeSelector, affinity rules, and taints and tolerations. Without any hints, the scheduler spreads Pods to balance the cluster.
A fresh cluster ships with default (where your objects land if you don't pick one), kube-system (control plane and system components), kube-public (world-readable cluster info), and kube-node-lease (node heartbeat objects for faster failure detection).
You put your workloads in default only for quick tests. Real setups create their own namespaces per team or environment and leave kube-system alone.
Self-healing is Kubernetes automatically recovering from failures without a human. If a container crashes, the kubelet restarts it. If a Pod dies, its controller recreates it. If a node fails, its Pods get rescheduled elsewhere. If a probe says a container is unhealthy, Kubernetes acts on it.
This falls out of the control loop: controllers constantly compare desired state to actual state and fix the difference. Self-healing isn't a feature you turn on, it's how the system works.
Key point: self-healing maps back to the control loop. Interviewers love when a candidate connects a feature to the underlying reconciliation model.
Every object needs four top-level fields: apiVersion (which API group and version), kind (the object type like Pod or Service), metadata (at least a name), and spec (the desired state, whose shape depends on the kind).
Kubernetes reads spec as what you want and reports the current status back on the object. You write spec; the system fills in status.
apiVersion: apps/v1 # API group and version
kind: Deployment # object type
metadata: # name, labels, namespace
name: web
spec: # desired state
replicas: 3For candidates with working experience: object mechanics, networking, storage, and the questions that separate users from operators.
Use a Deployment for stateless apps where Pods are interchangeable. Use a StatefulSet when each Pod needs a stable identity: a fixed name, stable network hostname, and its own persistent volume that follows it across restarts.
Databases, message brokers, and clustered systems that care which replica is which are StatefulSet territory. A StatefulSet also rolls out and scales Pods in order (0, 1, 2), which ordered systems rely on.
| Deployment | StatefulSet | |
|---|---|---|
| Pod identity | Interchangeable, random names | Stable, ordered names (web-0, web-1) |
| Storage | Usually shared or none | Per-Pod persistent volume |
| Rollout order | All at once within limits | Ordered, one at a time |
| Typical use | Stateless web and API | Databases, brokers, clustered state |
Key point: The signal here is knowing that StatefulSets are not just Deployments with storage: stable identity and ordered operations are the point.
Watch a deeper explanation
Video: Kubernetes Course - Full Beginners Tutorial (Containerize Your Apps!) (freeCodeCamp.org, YouTube)
A DaemonSet runs one copy of a Pod on every node (or every node matching a selector). When nodes join the cluster, they automatically get the Pod; when they leave, the Pod goes with them.
It's the tool for per-node agents: log collectors, monitoring agents, and network plugins. You use a DaemonSet whenever the workload's job is tied to the node itself rather than to a replica count.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: log-agent
spec:
selector:
matchLabels:
app: log-agent
template:
metadata:
labels:
app: log-agent
spec:
containers:
- name: agent
image: fluent-bit:3A Job runs a Pod to completion: it starts, does work, and succeeds when the work finishes, retrying on failure up to a limit. It's for batch tasks like a migration or a one-off computation.
A CronJob creates Jobs on a schedule, using cron syntax. It's for recurring work like nightly backups or hourly reports. So a CronJob is a Job with a timer.
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-backup
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: backup-tool:1A liveness probe checks if the container is still healthy; on failure Kubernetes restarts it. A readiness probe checks if the container can serve traffic; on failure the Pod is removed from Service endpoints but not restarted. A startup probe protects slow-starting apps by delaying the other two probes until the app has booted.
The classic mistake is using a liveness probe where you need a readiness probe, which causes needless restarts of a Pod that's just temporarily busy. Get this distinction right and you've answered a common gotcha.
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5| Probe | On failure | Use it for |
|---|---|---|
| Liveness | Restart the container | Detecting a hung or deadlocked process |
| Readiness | Remove Pod from Service endpoints | Temporary not-ready states, warmup, dependencies down |
| Startup | Delay other probes, then restart if it never passes | Slow-booting legacy apps |
Key point: Interviewers probe 'what happens if liveness fails vs readiness fails?'. Restart vs remove-from-traffic is the answer they want.
A request is what a container is guaranteed and what the scheduler uses to place the Pod: a Pod only lands on a node with enough unrequested capacity. A limit is the ceiling the container can't exceed at runtime.
Exceeding a memory limit gets the container OOM-killed. Exceeding a CPU limit throttles it, it doesn't die. Setting requests too low overcommits nodes; setting limits too tight causes throttling and kills. Getting these right is core production tuning.
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 500m
memory: 512MiKey point: The distinction that is technically useful: memory over-limit means OOMKill, CPU over-limit means throttling. Many candidates conflate the two.
A rolling update replaces old Pods with new ones gradually so the app stays available. The Deployment brings up new Pods, waits for them to pass readiness, then removes old ones, repeating until the whole set is updated.
You tune it with maxUnavailable (how many Pods can be down during the rollout) and maxSurge (how many extra Pods can exist above the desired count). Readiness probes are what make it safe: without them, traffic hits Pods that aren't ready.
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1How a rolling update proceeds
kubectl rollout undo returns to the previous ReplicaSet, which is why the old one is kept around.
Watch a deeper explanation
Video: What is Kubernetes | Kubernetes explained in 15 mins (TechWorld with Nana, YouTube)
Use kubectl rollout undo, which returns the Deployment to its previous ReplicaSet. Because Kubernetes keeps the old ReplicaSet around (limited by revisionHistoryLimit), rollback is just scaling the old set back up and the new one down.
You can inspect history with kubectl rollout history and roll back to a specific revision. Pausing a rollout mid-flight with kubectl rollout pause is the other useful control when you spot trouble.
kubectl rollout status deployment/web
kubectl rollout history deployment/web
kubectl rollout undo deployment/web
kubectl rollout undo deployment/web --to-revision=3A Service exposes Pods at the network layer (L4): a stable IP and port. An Ingress works at the HTTP layer (L7): it routes external HTTP and HTTPS traffic to different Services based on hostname and path, and handles TLS termination.
An Ingress needs an Ingress controller (like NGINX or Traefik) actually running in the cluster to enforce the rules. Without a controller, an Ingress object does nothing. It lets many services share one external entry point instead of one LoadBalancer each.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: site
spec:
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80Key point: The gotcha is forgetting the Ingress controller. Saying 'an Ingress does nothing without a controller' is exactly the awareness interviewers check.
A PersistentVolume (PV) is a piece of storage in the cluster. A PersistentVolumeClaim (PVC) is a Pod's request for storage of a given size and access mode. Kubernetes binds a claim to a matching volume, and the Pod mounts the claim.
A StorageClass automates the PV side: instead of an admin pre-creating volumes, the class provisions one dynamically when a claim asks for it. So the everyday flow is Pod to PVC to StorageClass to a freshly provisioned PV.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
storageClassName: standard
resources:
requests:
storage: 10GiKubernetes runs a DNS service (CoreDNS) that gives every Service a DNS name. A Service named web in namespace shop resolves at web.shop.svc.cluster.local, and from within the same namespace just web works.
This is why apps address each other by Service name instead of IP. Pods are configured to use CoreDNS automatically, so service discovery is built in with no extra tooling.
# from a Pod in the same namespace
curl http://web
# fully qualified, from anywhere
curl http://web.shop.svc.cluster.localThe Service controller keeps an Endpoints (or EndpointSlice) list of the IPs of Pods matching the selector and passing readiness. On each node, kube-proxy programs the kernel (iptables or IPVS rules) so that traffic to the Service's ClusterIP is redirected to one of those Pod IPs.
So there's no single proxy process the packets flow through by default; kube-proxy sets up rules and the kernel does the load-balancing. When a Pod becomes unready, it drops out of the endpoint list and stops receiving traffic.
Key point: kube-proxy and iptables or IPVS matters. That depth stands out.
A NetworkPolicy controls which Pods can talk to which, at the IP and port level. By default all Pods can reach all other Pods; a NetworkPolicy lets you restrict that, for example only letting the frontend reach the backend.
Policies select Pods by label and define allowed ingress and egress. One catch: they only take effect if your CNI network plugin supports them (Calico, Cilium do; some don't), so a policy on an unsupporting plugin silently does nothing.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-allow-api
spec:
podSelector:
matchLabels:
app: db
ingress:
- from:
- podSelector:
matchLabels:
app: apiTwo main ways: as environment variables or as mounted files. You pull values from a ConfigMap or Secret using valueFrom for individual env vars, envFrom to load a whole ConfigMap, or a volume mount to expose the data as files.
Mounting as files has an edge: file-mounted ConfigMap changes propagate to the Pod without a restart, while env vars are fixed at container start. For secrets, file mounts are also generally safer than env vars.
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
envFrom:
- secretRef:
name: db-credsInit containers run to completion before the app containers in a Pod start, one after another in order. If an init container fails, the Pod restarts it until it succeeds, so the main app never runs until preconditions are met.
Use them for setup work: waiting for a database to be reachable, running a schema migration, or fetching config into a shared volume. They keep that logic out of your app image.
spec:
initContainers:
- name: wait-for-db
image: busybox:1.36
command: ['sh', '-c', 'until nc -z db 5432; do sleep 2; done']
containers:
- name: app
image: myapp:1The Horizontal Pod Autoscaler (HPA) automatically changes the number of Pod replicas based on observed metrics, most commonly CPU or memory usage, or custom metrics. When load rises past a target, it adds Pods; when load drops, it removes them.
It needs resource requests set (so it has a baseline to measure against) and a metrics source like the metrics-server. HPA scales Pod count; the Vertical Pod Autoscaler changes per-Pod resources instead.
kubectl autoscale deployment web --cpu-percent=70 --min=2 --max=10
kubectl get hpaA taint on a node repels Pods: by default no Pod will schedule there. A toleration on a Pod lets it ignore a matching taint and schedule anyway. Together they reserve nodes for specific workloads.
The classic use is dedicated hardware: taint the GPU nodes so only Pods that tolerate the taint (the ones that actually need GPUs) land there. Taints and tolerations repel; node affinity attracts. You often use them together.
kubectl taint nodes gpu-node-1 gpu=true:NoSchedule
# Pod toleration:
# tolerations:
# - key: gpu
# operator: Equal
# value: 'true'
# effect: NoScheduleNode affinity attracts Pods to nodes with certain labels, like scheduling only on nodes in a specific zone or with SSDs. Pod affinity and anti-affinity place Pods relative to other Pods: affinity co-locates them, anti-affinity spreads them apart.
Anti-affinity is how you keep replicas of a Deployment on different nodes so a single node failure doesn't take them all down. Rules come in two strengths: required (hard) and preferred (soft, best effort).
kubectl get pods to read the status, then kubectl describe pod for events (scheduling failures, image pull errors, probe failures show up here), then kubectl logs for what the app printed comes first. For a shell inside a running container, kubectl exec.
The status tells you which direction to look: Pending means a scheduling problem, ImagePullBackOff means a bad image or registry auth, CrashLoopBackOff means the app keeps exiting, so read the logs. A methodical get, describe, logs, exec loop solves most issues.
kubectl get pods
kubectl describe pod web
kubectl logs web --previous
kubectl exec -it web -- shA debugging loop for a broken Pod
The Pod status usually points at the layer to inspect, so let it steer the loop rather than guessing.
Key point: The technical decision depends on whether you have an ordered method (get, describe, logs, exec), not whether you memorized a rare flag.
Watch a deeper explanation
Video: Kubernetes Crash Course for Absolute Beginners [NEW] (TechWorld with Nana, YouTube)
CrashLoopBackOff means a container starts, then exits or fails, and the kubelet keeps restarting it with a growing back-off delay. It's a symptom, not a root cause. Common causes: the app crashes on a config or dependency error, a failing liveness probe kills a healthy-but-slow app, a bad command, or a missing environment variable.
Diagnose with kubectl logs --previous to see the last crash, and kubectl describe pod for probe and exit-code details. Exit code 137 points at an OOM kill; a stack trace points at an app bug; a connection error points at a missing dependency.
kubectl logs web --previous
kubectl describe pod web | grep -A5 'Last State'Key point: Naming CrashLoopBackOff as a symptom, then listing the real causes (app crash, bad probe, OOM), is what separates an operator from someone who's only read docs.
Helm is a package manager for Kubernetes. A Helm chart bundles the templated YAML for an app, and you install it with one command, passing values to customize each environment. It turns dozens of manifests into a single versioned, parameterized package.
It solves two real problems: reuse (install the same charted app across environments with different values) and lifecycle (upgrade and roll back releases as units). The trade-off is another templating layer to learn and debug.
helm install web ./web-chart -f values-prod.yaml
helm upgrade web ./web-chart --set replicaCount=5
helm rollback web 1A ResourceQuota caps the total resources (CPU, memory, object counts) a namespace can consume, so one team can't starve the cluster. A LimitRange sets default and maximum requests and limits per Pod or container in a namespace.
Used together, a LimitRange gives every Pod sane defaults while the ResourceQuota enforces a hard ceiling on the namespace as a whole. This is standard multi-tenant hygiene.
advanced rounds probe internals, architecture, and production scars. Expect every answer here to draw a follow-up.
Every controller runs the same loop: watch the API server for a resource's desired state, compare it to observed state, and take actions to close the gap, then repeat forever. The Deployment controller, the scheduler, the kubelet, and custom operators all follow this pattern.
This is why Kubernetes is level-triggered, not edge-triggered: it reacts to the current state, not to a one-time event, so a missed event or a controller restart doesn't break anything. The controller just re-reads state and reconciles again. That property is what makes the system self-correcting.
Key point: Saying level-triggered vs edge-triggered and explaining why it makes controllers resilient is a strong production signal. It shows you understand the design, not just the objects.
etcd is the cluster's datastore: a distributed key-value store holding all cluster state (every object, spec, and status). The API server is the only component that talks to it directly. If etcd is lost, the cluster's state is lost.
Operational concerns: run an odd number of members (3 or 5) for quorum, back it up regularly because it's the single source of truth, watch its disk latency because slow etcd slows the whole API, and encrypt Secrets at rest since etcd stores them.
Key point: the rule is 'etcd is the source of truth; back it up and give it fast disks.' quorum and encryption at rest matters.
kubectl sends the manifest to the API server. The request passes through authentication (who are you), authorization (RBAC: are you allowed), and admission control (mutating webhooks that alter the object, then validating webhooks and quota checks). If it passes, the API server persists the object to etcd and returns success.
From there it's asynchronous: controllers watching that resource notice the change and reconcile. For a Deployment, the Deployment controller makes a ReplicaSet, which makes Pods, the scheduler assigns nodes, and kubelets start containers. apply returns before any of that finishes.
Key point: The insight that scores: apply just records desired state; the real work is controllers reconciling afterward. Candidates who think apply does the work directly miss the model.
RBAC grants permissions through roles and bindings. A Role (namespaced) or ClusterRole (cluster-wide) lists allowed verbs (get, list, create) on resources. A RoleBinding or ClusterRoleBinding attaches that role to a subject: a user, group, or ServiceAccount.
Permissions are additive and default-deny: if no rule allows an action, it's denied. The everyday production use is giving each app a ServiceAccount with the least privilege it needs, rather than running Pods with broad cluster access.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: shop
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]A CustomResourceDefinition (CRD) extends the Kubernetes API with your own object type, so kubectl get postgres works just like kubectl get pods. An operator is a custom controller that watches those custom resources and runs the same reconciliation loop to manage a real system.
The operator pattern encodes operational knowledge as code: an operator for a database can handle provisioning, backups, failover, and upgrades by reacting to the custom resource's desired state. It's how you teach Kubernetes to run complex stateful software.
Set a securityContext: run as a non-root user, drop all Linux capabilities and add back only what's needed, make the root filesystem read-only, and disallow privilege escalation. At the namespace level, Pod Security Admission enforces baseline or restricted profiles to reject unsafe Pods.
PodSecurityPolicy is gone (removed in 1.25); the current answer is Pod Security Admission plus, for finer control, a policy engine like OPA Gatekeeper or Kyverno. Naming that the old PSP is deprecated shows current knowledge.
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ['ALL']Key point: Mentioning that PodSecurityPolicy was removed in 1.25 and replaced by Pod Security Admission is a fast way to show you track current Kubernetes.
Every Pod runs as a ServiceAccount (the default one unless you specify another). Kubernetes mounts a short-lived, automatically rotated token into the Pod, and the app or a client library uses it to call the API server, where RBAC decides what it can do.
Two hardening points: give each workload its own ServiceAccount with least-privilege RBAC rather than the default, and set automountServiceAccountToken: false on Pods that never call the API, so they don't carry a credential they don't need.
The HPA scales the number of Pods based on load. The Cluster Autoscaler scales the number of nodes: when Pods can't be scheduled because the cluster is out of capacity, it adds nodes; when nodes sit underused, it drains and removes them.
They work together: HPA adds Pods, those Pods go Pending because there's no room, and the Cluster Autoscaler notices and adds a node. Knowing they operate at different layers (Pods vs nodes) is the point of the question.
| Autoscaler | Scales | Trigger |
|---|---|---|
| Horizontal Pod Autoscaler | Pod replica count | Metrics like CPU, memory, custom |
| Vertical Pod Autoscaler | Per-Pod CPU and memory requests | Observed usage over time |
| Cluster Autoscaler | Number of nodes | Unschedulable Pods or idle nodes |
Blue-green runs two full environments (old and new) and flips a Service selector or Ingress from blue to new all at once, with instant rollback by flipping back. Canary sends a small slice of traffic to the new version first, watches metrics, then ramps up if healthy.
Plain Kubernetes can approximate both by juggling labels, Services, and replica counts, but the honest coverage names the real tools: a service mesh (Istio, Linkerd) for percentage-based traffic splitting, or a progressive-delivery controller like Argo Rollouts or Flagger that automates the canary and its rollback.
Reasons to split: hard isolation between environments or tenants, blast-radius reduction (a bad change can't take everything down), regulatory or data-residency boundaries, and staying under a single cluster's scaling limits. Reasons to stay single: lower operational overhead and simpler networking and cost.
The trade-off is complexity: multiple clusters mean multi-cluster deployment tooling, cross-cluster networking, and consistent policy everywhere. The production-ready answer weighs isolation and blast radius against that operational cost, rather than reflexively picking one.
When a node stops reporting, it's marked NotReady. After a grace period, the node controller evicts its Pods, and controllers reschedule the ones they own onto healthy nodes. Pods that belong to no controller (bare Pods) are just lost.
Response: check whether it's the kubelet, the network, or the machine itself (kubectl describe node, then the node's own logs). If the node won't recover, cordon it so nothing new schedules there, drain it to move workloads off gracefully, and replace it. This is where PodDisruptionBudgets matter, so draining doesn't take too many replicas down at once.
kubectl describe node node-3
kubectl cordon node-3
kubectl drain node-3 --ignore-daemonsets --delete-emptydir-dataA PodDisruptionBudget (PDB) limits how many Pods of an app can be voluntarily disrupted at once, for example during a node drain or cluster upgrade. You set minAvailable or maxUnavailable, and voluntary evictions respect it.
It matters for availability during maintenance: without a PDB, draining several nodes could evict every replica of a service simultaneously. A PDB tells Kubernetes to keep enough replicas running so the drain proceeds gradually. It only guards voluntary disruptions, not a node crashing.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: webKey point: The distinction to state clearly: a PDB protects against voluntary disruptions (drains, upgrades), not involuntary ones (a node dying). Getting that boundary right is the tell.
Base64 in a Secret object is not enough. Enable encryption at rest so etcd doesn't store plaintext, lock down access with RBAC, and prefer mounting secrets as files over environment variables. Never commit secrets to Git; if you use GitOps, encrypt them first with a tool like Sealed Secrets or SOPS.
For stronger setups, pull secrets from an external manager (Vault, a cloud secret store) via the External Secrets Operator or CSI driver, so the source of truth lives outside the cluster and rotation is centralized.
Three pillars: metrics (Prometheus scraping cluster and app metrics, Grafana for dashboards), logs (a collector like Fluent Bit shipping container logs to a backend), and traces (OpenTelemetry into a tracing backend). The metrics-server is a lighter piece that just feeds the HPA and kubectl top.
What to actually watch: Pod restart counts and CrashLoopBackOff, node resource pressure, pending Pods (a scheduling or capacity signal), and per-service latency and error rates. Alerting on those catches most problems before users do.
GitOps makes a Git repository the source of truth for cluster state. A controller in the cluster (Argo CD or Flux) continuously compares the live cluster to the manifests in Git and reconciles any drift, so deployments happen by merging a pull request, not by running kubectl.
The wins: every change is reviewed and audited in Git, rollback is a git revert, and the cluster self-heals back to the declared state if someone changes it manually. It's the reconciliation model extended from inside the cluster out to your source control.
An OOMKill means a container hit its memory limit and the kernel killed it; you'll see exit code 137 and OOMKilled in the Pod's last state. First confirm it's the limit and not the whole node running out of memory, which evicts Pods differently.
Fix by measuring real usage (kubectl top pod, or historical metrics) and setting the memory limit above the true working set with headroom, or fixing an actual leak in the app. Setting limits from guesses is the usual root cause: too low and you OOMKill healthy apps, too high and you overcommit the node.
kubectl top pod web
kubectl describe pod web | grep -A3 'Last State'
# look for Reason: OOMKilled, Exit Code: 137Key point: Knowing exit code 137 equals OOMKilled, and distinguishing a per-container limit kill from node-level memory eviction, is the detail that indicates real production experience.
Upgrade the control plane first, then nodes, one minor version at a time (you can't skip minors). Check the release notes for deprecated and removed APIs, and scan your manifests for anything that will break before upgrading. Back up etcd first.
For nodes, roll through them: cordon and drain one, upgrade the kubelet, uncordon, and move on, respecting PodDisruptionBudgets so services stay available. Managed offerings (GKE, EKS, AKS) automate much of this but the same order and the API-deprecation check still apply.
The model has flat rules: every Pod gets its own IP, every Pod can reach every other Pod without NAT, and nodes can reach all Pods. Kubernetes itself doesn't implement this; it delegates to a CNI plugin (Calico, Cilium, Flannel) that wires up Pod networking on each node.
The CNI choice affects real features: some plugins enforce NetworkPolicies and some don't, and options like Cilium use eBPF for performance and observability. So 'which CNI' is an architecture decision, not a detail, because it decides what network security you can even enforce.
The hard parts are storage and identity: data must survive Pod rescheduling (persistent volumes bound correctly), each replica needs a stable identity (StatefulSets), and failover and backups must be orchestrated. Storage performance and the fact that a volume is usually tied to one zone add scheduling constraints.
The practical answer: use a purpose-built operator that encodes the database's operational knowledge (failover, backup, restore) rather than hand-rolling StatefulSets, and honestly weigh whether a managed database outside the cluster is the better call. Senior candidates name that trade-off instead of assuming everything belongs in Kubernetes.
Clarify the isolation requirement first (soft multi-tenancy between trusted internal teams vs hard isolation for untrusted tenants), because that decides whether one cluster is even acceptable. For internal teams, a namespace per team is the backbone.
Then layer the controls: ResourceQuotas and LimitRanges so no team starves the cluster, RBAC scoped per namespace, NetworkPolicies to stop cross-team traffic by default, Pod Security Admission for a safe baseline, and separate node pools with taints for noisy or sensitive workloads. If isolation must be hard, the honest answer is separate clusters, because namespaces share a kernel and a control plane.
Key point: The structure the technical evaluation checks: clarify the isolation level, then map each requirement (fairness, access, network, security) to a specific mechanism, and know when namespaces aren't enough.
Kubernetes wins when you run many services that need self-healing, autoscaling, and rich networking across a fleet of machines. It trades a steep learning curve and operational weight for control loops, a large ecosystem, and portability across clouds. Where it loses is small deployments: for one host or a handful of containers, Docker Compose or Swarm is far less to learn and operate, and Nomad is lighter when you also schedule non-container workloads. Saying these trade-offs out loud is itself an interview signal: it shows you pick tools on fit, not hype.
| Tool | Scope | Best at | Watch out for |
|---|---|---|---|
| Kubernetes | Multi-node orchestration | Self-healing, autoscaling, large ecosystem | Steep learning curve, operational weight |
| Docker Swarm | Multi-node, Docker-native | Simple clustering with familiar Docker CLI | Smaller ecosystem, less active development |
| Nomad | Multi-node scheduler | Mixed container and non-container workloads | Fewer built-in networking and storage features |
| Docker Compose | Single host | Local dev and small single-machine stacks | No clustering, scheduling, or self-healing |
Prepare in layers, and practice on a real cluster. Most Kubernetes rounds move from concept questions to reading or writing YAML to a debugging or design discussion, so rehearse each stage rather than only reading answers.
The typical Kubernetes interview flow
Earlier rounds increasingly run as AI coding interviews. The objects and control-loop model on this page are what get probed at every stage.
10 questions, about 6 minutes. Score 70% or higher to earn a shareable certificate.
Hyring builds the AI Coding Interviewer that runs live technical rounds in 20+ languages for 5,000+ hiring teams. These Kubernetes questions and scoring notes reflect what actually gets asked and evaluated inside the interviews we host.
See how the AI Coding Interviewer works