Kubernetes interviews test whether you understand the core orchestration primitives and how they fit together in production. These are the questions asked most often for DevOps and platform roles.
Kubernetes automates deployment, scaling, and management of containerized applications across a cluster of machines. It handles scheduling containers onto available nodes, restarting failed containers, scaling replicas up or down based on load, rolling out updates without downtime, and service discovery/load balancing between containers — replacing what would otherwise be a lot of custom orchestration scripting.
A Pod is the smallest deployable unit — one or more tightly-coupled containers sharing network and storage. A Deployment manages a desired set of Pod replicas, handling rolling updates and self-healing by recreating Pods that crash or land on failed nodes. A Service provides a stable network endpoint (a fixed IP and DNS name) that load-balances traffic to a dynamic, changing set of Pod IPs, since individual Pods are ephemeral and get replaced with new IPs constantly.
Both externalize configuration from container images so you don't have to rebuild images for config changes. ConfigMaps store non-sensitive plain-text configuration like feature flags or URLs. Secrets store sensitive data such as passwords, API keys, and tokens — base64-encoded by default, which is encoding, not encryption, so at-rest encryption requires enabling encryption at the etcd level separately. Kubernetes also applies tighter access controls to Secrets and many tools avoid printing their values in logs or `kubectl describe` output.
Horizontal Pod Autoscaling (HPA) automatically adjusts the number of Pod replicas in a Deployment (or other scalable workload) based on observed metrics like CPU/memory utilization or custom metrics. Cluster Autoscaling separately adjusts the number of underlying nodes in the cluster when Pods can't be scheduled due to insufficient capacity, and scales nodes down when they're underutilized. These work together — HPA scales Pods, and if there's no room on existing nodes, the cluster autoscaler provisions new ones (and there's also Vertical Pod Autoscaling, which adjusts a container's requests/limits rather than replica count).
A liveness probe checks whether a container is still functioning correctly — if it fails past a configured threshold, Kubernetes kills and restarts the container, since a hung or deadlocked process would otherwise keep running indefinitely. A readiness probe checks whether a container is ready to accept traffic — if it fails, Kubernetes stops routing Service traffic to that Pod without restarting it, which is useful during startup or when a Pod is temporarily overloaded and shouldn't receive new requests. There's also a startup probe, which delays the other two from running until a slow-starting container has finished initializing, preventing a liveness probe from killing a container that just needed more time to boot.
A rolling update gradually replaces old Pod replicas with new ones matching the updated Deployment spec, controlled by maxSurge (how many extra Pods can be created above the desired count during the rollout) and maxUnavailable (how many Pods can be unavailable at once). This achieves zero-downtime deployments as long as readiness probes correctly gate traffic to only fully-ready new Pods, and a rollout can be paused, resumed, or rolled back with `kubectl rollout undo` if new Pods start failing.
etcd is a distributed, consistent key-value store that serves as the cluster's single source of truth for all state — every object (Pods, Deployments, Services, ConfigMaps, and so on) is persisted there. The API server is the only component that talks to etcd directly; all other components (scheduler, controllers, kubelets) interact with cluster state exclusively through the API server, which reads and writes to etcd on their behalf. Because it's the source of truth, etcd availability and backups are critical — losing etcd data effectively means losing the cluster's state.
A Namespace logically partitions a single cluster into multiple virtual clusters, scoping resource names, RBAC policies, and resource quotas so that names like "frontend" don't collide across teams or environments. They're commonly used to separate environments (dev, staging, prod) or teams sharing the same physical cluster, and you can apply ResourceQuotas and NetworkPolicies per namespace to limit what each group can consume or reach. Not everything is namespaced, though — cluster-wide objects like Nodes, PersistentVolumes, and ClusterRoles exist outside any namespace. Namespaces alone don't provide strong multi-tenancy or hard security isolation; that needs additional controls like NetworkPolicies and RBAC, or separate clusters for stricter boundaries.
A Deployment treats Pods as interchangeable — replicas get random names and IPs and can be replaced in any order, since the workload is assumed to be stateless. A StatefulSet is designed for stateful workloads like databases or message queues that need stable, predictable identities: each Pod gets a fixed ordinal name (like db-0, db-1), a stable network identity via a headless Service, and its own PersistentVolumeClaim that follows it across rescheduling. StatefulSets also create and scale Pods in order (0, 1, 2, ...) and tear them down in reverse order by default, which matters for clustered systems that expect members to join or leave predictably. Use a Deployment for stateless, horizontally-scalable services, and a StatefulSet when Pod identity or per-Pod storage must persist.
A DaemonSet ensures that exactly one copy of a Pod runs on every node in the cluster, or on a selected subset of nodes via node selectors or affinity, automatically adding a Pod when a new node joins and removing it when a node leaves. It's the standard pattern for node-level infrastructure like log collectors, monitoring agents, or CNI/CSI networking and storage plugins that need to run on every machine rather than being scheduled like a normal replicated workload. Because DaemonSet Pods are tied to specific nodes, they bypass the scheduler's normal placement logic and typically carry tolerations for taints like the control-plane taint so they can also run on master nodes when needed.
ClusterIP is the default Service type — it exposes the Service only on an internal, cluster-reachable IP, used for Pod-to-Pod communication within the cluster. NodePort opens the same static port on every node's IP and forwards traffic from that port to the Service, giving external access without a cloud load balancer, though it's clunky for production use. LoadBalancer provisions an external cloud load balancer (on AWS, GCP, Azure, etc.) that routes traffic to the Service, typically implemented on top of NodePort under the hood. Ingress isn't a Service type at all — it's a separate API object that sits in front of one or more Services and provides HTTP/HTTPS routing such as host- and path-based rules and TLS termination through a single external entry point, and it requires an Ingress controller like nginx or Traefik to actually implement those routing rules.
Helm is a package manager for Kubernetes that bundles a set of related manifests — Deployments, Services, ConfigMaps, and so on — into a versioned, reusable unit called a chart. Instead of hand-writing and templating raw YAML for every environment, you parameterize a chart with values files and install or upgrade it with a single command, and Helm tracks releases so you can roll back to a previous revision if an upgrade goes wrong. It's especially useful for installing third-party software, like an ingress controller or a database operator, where maintainers publish a chart with sensible defaults, and for managing your own app's per-environment config through different values files rather than duplicating YAML.
A request is the amount of CPU or memory the scheduler guarantees is reserved for a container when placing it on a node — the scheduler will only place a Pod on a node that has enough unreserved capacity to satisfy its requests. A limit is the hard ceiling a container can't exceed: going over a memory limit gets the container OOM-killed, while going over a CPU limit just gets the container throttled rather than killed. These settings determine a Pod's QoS class: Guaranteed (every container has requests equal to limits, for both CPU and memory), Burstable (at least one container has requests set but they don't equal limits), or BestEffort (no requests or limits set at all). QoS class matters most when a node runs low on memory — the kubelet evicts BestEffort Pods first, then Burstable, and only evicts Guaranteed Pods as a last resort.
Both influence Pod scheduling, but from opposite directions. Node affinity is attractive and lives on the Pod — it lets you say "prefer or require scheduling onto nodes with these labels," such as a specific instance type or zone, with both a "required" hard-constraint form and a "preferred" soft, best-effort form. Taints and tolerations work the other way: a taint is applied to a node to repel Pods by default, meaning don't schedule here unless a Pod specifically tolerates it, and a Pod needs a matching toleration to be allowed onto that node. A common pattern combines both — taint a pool of GPU nodes so ordinary Pods won't land there, then give only the GPU-needing Pods both a toleration for that taint and a node affinity or selector actually steering them onto those nodes.
A PersistentVolume (PV) represents an actual piece of storage in the cluster, provisioned either ahead of time by an admin (static) or on demand by a StorageClass's provisioner (dynamic), backed by something like an EBS volume, an NFS share, or a cloud disk. A PersistentVolumeClaim (PVC) is a request for storage made by a user or Pod, specifying size and access mode, and Kubernetes binds it to a matching PV or dynamically creates one via a StorageClass. Pods reference the PVC, not the PV directly, in their volume spec, which decouples application manifests from the underlying storage implementation. This separation lets a Pod be rescheduled to a different node while keeping the same underlying data, as long as the storage backend supports being attached from wherever the Pod lands.
The kube-scheduler watches for newly created Pods that don't yet have a node assigned and decides which node each one should run on. It does this in two phases: filtering, where it eliminates nodes that can't run the Pod due to insufficient resources, unmatched taints/tolerations, or unsatisfied affinity/anti-affinity rules, and scoring, where it ranks the remaining feasible nodes by criteria like resource balance and affinity preferences to pick the best fit. Once it picks a node, the scheduler writes that binding back to the API server, and therefore to etcd — it doesn't start the container itself; the kubelet on the chosen node watches for that assignment and takes over actually running it. Custom scheduling needs are typically handled with scheduler extenders, a second scheduler, or scheduling profiles rather than modifying the default scheduler.
A Job runs one or more Pods to completion for a finite task, like a batch import or a one-off migration script, retrying failed Pods up to a configured limit, and it's considered done once the specified number of successful completions is reached — unlike a Deployment, it doesn't keep the Pod running indefinitely. A CronJob is a wrapper around Job that creates new Jobs on a recurring schedule defined with standard cron syntax, useful for periodic tasks like nightly backups or report generation. CronJobs have settings to control overlapping runs, via concurrencyPolicy set to Allow, Forbid, or Replace, and how many old completed or failed Job objects to retain for history, since each scheduled run leaves behind Job and Pod objects that would otherwise accumulate.
Role-Based Access Control governs who can do what against the Kubernetes API. A Role, or a ClusterRole for cluster-wide or non-namespaced resources, defines a set of permissions — verbs like get, list, create, or delete — against specific resource types, and a RoleBinding or ClusterRoleBinding attaches that Role to a subject: a user, a group, or a ServiceAccount. Roles are namespace-scoped, so a Role only grants permissions within the namespace it's defined in, while ClusterRoles can be bound cluster-wide via a ClusterRoleBinding or scoped to a single namespace via a RoleBinding. In practice, ServiceAccounts are the more common subject inside the cluster — Pods use them to authenticate to the API server — and granting a workload only the specific verbs and resources it needs, rather than broad access, is the standard least-privilege approach.
kubectl is just a client for the Kubernetes API server — it doesn't talk to nodes, etcd, or anything else directly. It reads a kubeconfig file, by default ~/.kube/config, containing the API server's URL, a CA certificate to verify the server, and credentials — a client certificate, a bearer token, or an exec-based plugin for cloud IAM auth — for one or more clusters and contexts, and the current context determines which cluster, credentials, and namespace a command targets. Every kubectl command translates to an HTTPS REST call against the API server (get, list, create, patch, delete, and so on), which authenticates the request, runs it through RBAC authorization and admission control, then reads or writes the result to etcd. This is why kubectl works identically against any cluster you have valid kubeconfig credentials for — it isn't doing anything special, it's using the same API any other client, like a controller or the dashboard, uses.