The 60 DevOps questions interviewers actually ask, with direct answers, real manifests and commands, and what the interviewer is listening for. Grouped for freshers, intermediate, and experienced rounds.
60 questions with answersKey Takeaways
DevOps is a way of working that brings development and operations together so software ships faster and runs more reliably. It's a culture first (shared ownership, small frequent changes, blameless learning) and a set of practices second (continuous integration and delivery, infrastructure as code, monitoring and observability, automated recovery). Calling it a job title alone misses the point, though "DevOps engineer" is a common role that builds and runs the automation that makes those practices real. In interviews, DevOps questions probe how you reason about trade-offs: containers versus virtual machines, when Kubernetes earns its complexity, how you deploy without downtime, and what you'd do at 3am when a release goes wrong. This page collects the 60 questions that come up most, each with a direct answer plus real manifests and commands. The Kubernetes concepts documentation is the canonical reference for the orchestration half; pair this bank with our AI interview preparation guides for the live-interview format, since the first DevOps round is increasingly an AI-driven technical screen.
Watch: What is DevOps? REALLY understand it | DevOps vs SRE
Video: What is DevOps? REALLY understand it | DevOps vs SRE (TechWorld with Nana, YouTube)
Test yourself and earn a certificate
10 quick questions. Score 70%+ to download your DevOps certificate.
The fundamentals every entry-level round checks: what DevOps is, CI/CD, containers, and the Git and Linux basics underneath. If any answer here surprises you, that's your study list.
DevOps is a culture and set of practices that shorten the loop between writing code and running it reliably in production. It brings developers and operations together around shared ownership, small frequent changes, and automation instead of manual handoffs.
Teams adopt it to ship faster with fewer failures: continuous integration and delivery catch problems early, infrastructure as code makes environments reproducible, and monitoring plus fast rollback keep incidents short. It's a way of working, not a single tool or job title, though "DevOps engineer" is a common role that builds the automation.
Key point: Lead with 'culture and practices, not just tools'. Interviewers open with this to hear whether you understand DevOps as a way of working or just a list of software.
Watch a deeper explanation
Video: DevOps Engineering Course for Beginners (freeCodeCamp.org, YouTube)
DevOps is the broad philosophy: dev and ops sharing responsibility for the whole lifecycle. SRE (Site Reliability Engineering) is one concrete implementation of it, with specific practices like SLOs, error budgets, and treating operations as a software problem.
Platform engineering is the newer name for building an internal platform (paved paths, self-service pipelines, golden templates) so product teams can ship without deep infrastructure knowledge. They overlap heavily; the honest coverage names the shared goal, reliable fast delivery, rather than drawing hard borders.
Key point: Don't claim they're rivals. Saying 'SRE is a specific way to do DevOps' signals you understand the relationship, which is exactly what this question checks.
Continuous integration (CI) means merging code to a shared branch often, with automated build and tests running on every change so integration problems surface within minutes, not weeks.
Continuous delivery keeps that branch always releasable and adds a manual approval to push to production. Continuous deployment removes the approval: every change that passes the pipeline ships to production automatically. Same acronym, one key difference: the human gate.
| Stage | What it automates | Human gate to prod? |
|---|---|---|
| Continuous integration | Build and test on every merge | N/A (not deploying) |
| Continuous delivery | Everything, up to a deployable artifact | Yes, manual promote |
| Continuous deployment | Everything through to production | No, fully automatic |
Key point: The trap is treating delivery and deployment as synonyms. The one-word answer is 'the manual gate to production'.
Watch a deeper explanation
Video: DevOps CI/CD Explained in 100 Seconds (Fireship, YouTube)
A typical pipeline runs commit, build, test, then deploy. A push triggers it, the code is built into an artifact or container image, automated tests and scans run, and if everything passes the artifact is promoted through environments to production.
The principle worth stating is build once, deploy everywhere: you build a single immutable artifact and promote that exact artifact through staging and production, rather than rebuilding at each stage. Rebuilding risks shipping something you never tested.
A typical CI/CD pipeline
The build-once, promote-everywhere rule matters: the artifact tested in staging is the exact one that reaches production.
Key point: Mention 'build once, promote everywhere'. It shows you understand why the artifact is immutable across environments, a common follow-up.
Automation makes releases repeatable, fast, and auditable. A manual deploy depends on one person's memory and steps; an automated one runs the same way every time, logs what it did, and can be triggered by anyone with the right permission.
The deeper payoff is small batch size: when deploying is cheap and safe, teams ship small changes often, which makes each change easier to review and to roll back. Big rare manual releases are where the scary incidents live.
Key point: automation connects to small batch size and fast rollback. That reasoning is more senior than 'it saves time'.
A build artifact is the packaged output of your build: a container image, a JAR, a binary, a tarball. It's what gets deployed. Immutable means once built and versioned, it never changes; a new build produces a new version rather than editing the old one.
Immutability is what makes 'the version in staging is exactly the version in production' true. If artifacts could change after the build, you'd be testing one thing and shipping another, and rollbacks would be unreliable.
The everyday set is branch and checkout to isolate work, add and commit to record changes, pull and push to sync with the remote, and merge or rebase to integrate. In a DevOps context you also lean on tags for releases, since a tag pins a commit that a pipeline can build and deploy.
Two that save you often: git revert to undo a bad change by creating a new commit (safe on shared history, unlike reset), and git log with a range to see exactly what shipped between two releases, which is how you build a changelog or figure out what a deploy included.
# cut a release the pipeline can build
git tag -a v1.5.0 -m "release 1.5.0"
git push origin v1.5.0
# what changed since the last release?
git log v1.4.0..v1.5.0 --oneline
# undo a bad commit safely on a shared branch
git revert <bad-commit-sha>Key point: Volunteering git revert over git reset for shared branches shows you understand safe history on a team, which is exactly the DevOps angle here.
Trunk-based development keeps everyone committing to one main branch (trunk) in small frequent merges, often behind feature flags. It pairs naturally with CI and continuous deployment because the branch stays releasable.
Gitflow uses long-lived branches: develop, release branches, hotfix branches, and feature branches that live for a while. It suits scheduled releases and multiple supported versions, but the long branches cause painful merges and slow integration, which is why many fast-shipping teams moved to trunk-based.
| Trunk-based | Gitflow | |
|---|---|---|
| Branch lifetime | Short, hours to a day | Long-lived develop/release branches |
| Best fit | Continuous delivery, feature flags | Scheduled releases, many versions |
| Main risk | Needs discipline and flags | Merge pain, slow integration |
Key point: If asked which you'd pick, it maps to release cadence: trunk-based for frequent shipping, Gitflow when releases are scheduled and versioned.
merge creates a merge commit that joins two branches, preserving the true branching history. rebase replays your commits on top of another branch, producing a straight linear history as if you'd branched from the latest point.
The practical rule: rebase your local, unpushed work to keep history clean, but never rebase commits others have already pulled, because rewriting shared history forces everyone else into painful conflicts. Merge is the safe choice for integrating shared branches.
# clean up local history before opening a PR
git fetch origin
git rebase origin/main
# integrate a shared branch (keeps the merge commit)
git checkout main
git merge --no-ff feature/loginKey point: The golden rule out loud: never rebase shared/pushed history. That single sentence is what the question is really checking.
A container shares the host operating system's kernel and isolates at the process level, so it's small and starts in milliseconds. A virtual machine runs a full guest OS with its own kernel on a hypervisor, so it isolates harder but is heavier and slower to boot.
The trade-off: containers pack densely and are ideal for microservices and consistent dev-to-prod environments; VMs give stronger isolation and can run different operating systems, which matters for multi-tenant or legacy workloads. Containers often run inside VMs in the cloud, so it's not either-or.
Key point: The follow-up is usually 'so are containers less secure?'. Acknowledge the weaker kernel isolation honestly rather than claiming they're identical to VMs.
A Docker image is a read-only template: your app plus its dependencies and a filesystem, built from a Dockerfile. A container is a running instance of an image, with a writable layer on top. One image can spawn many containers.
The mental model: image is to container as class is to object. You build and version images, push them to a registry, and run containers from them anywhere.
# build an image from a Dockerfile
docker build -t myapp:1.4 .
# run a container from that image
docker run -d -p 8080:8080 myapp:1.4
# many containers, one image
docker run -d myapp:1.4
docker run -d myapp:1.4Watch a deeper explanation
Video: Docker in 100 Seconds (Fireship, YouTube)
Each instruction in a Dockerfile creates a layer, and layers are cached and shared. If a layer hasn't changed, Docker reuses the cache instead of rebuilding, which makes builds fast and images smaller on disk because layers are shared across images.
The practical implication is ordering: put things that change rarely (installing dependencies) before things that change often (copying your source). Copy your lockfile and install dependencies first, then copy the rest of the code, so a code change doesn't bust the dependency-install cache.
# order Dockerfile steps least-to-most likely to change
COPY package.json package-lock.json ./
RUN npm ci # cached unless deps change
COPY . . # only this layer rebuilds on a code edit
RUN npm run buildKey point: Explaining the cache-ordering trick (dependencies before source) shows you've actually optimized a real Dockerfile, not just memorized a definition.
A registry is where you store and distribute container images: Docker Hub, GitHub Container Registry, Amazon ECR, Google Artifact Registry. You push images to it from CI and pull them onto whatever runs your containers.
Images are addressed by name and tag (myapp:1.4) or by an immutable digest (myapp@sha256:...). Pinning to a digest in production guarantees you run the exact bytes you tested, because a tag can be overwritten but a digest can't.
# tag and push to a registry
docker tag myapp:1.4 ghcr.io/acme/myapp:1.4
docker push ghcr.io/acme/myapp:1.4
# pull by immutable digest for reproducibility
docker pull ghcr.io/acme/myapp@sha256:9b2c...Docker Compose defines and runs a multi-container app from a single YAML file: your service, a database, a cache, all wired together with one command. It's built for local development and simple single-host setups.
It's not an orchestrator for production clusters; that's what Kubernetes is for. But for standing up a realistic local environment, or a small app on one server, Compose is the right, simple tool.
services:
web:
build: .
ports:
- "8080:8080"
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: devsecret
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:Key point: Say clearly that Compose is for local/single-host and Kubernetes is for production clusters. Confusing the two is a common junior slip.
A Dockerfile is the recipe (a text file of build instructions). Running docker build turns that recipe into an image (the packaged, versioned template). Running docker run turns an image into a container (a live, running process).
Recipe to template to running instance. Each step is deliberate: you edit the Dockerfile, rebuild the image, and run new containers from it.
Kubernetes is a container orchestrator: it runs your containers across a cluster of machines and handles the operational work you'd otherwise script by hand. You declare the desired state (run three replicas of this image, expose it on this port) and Kubernetes continuously works to match reality to that state.
It solves scheduling (which machine runs what), self-healing (restart or reschedule failed containers), scaling (add replicas under load), service discovery, and rolling updates. The cost is real complexity, which is why the honest answer also knows when not to use it.
Key point: The desired-state / reconciliation idea matters. It's the core concept every later Kubernetes question builds on.
Watch a deeper explanation
Video: Kubernetes Explained in 100 Seconds (Fireship, YouTube)
A Pod is the smallest deployable unit in Kubernetes: one or more tightly coupled containers that share a network namespace (same IP and port space) and can share storage volumes. Usually a Pod is one main container.
Pods are ephemeral and disposable: you don't nurse a Pod back to health, you let a controller replace it. You rarely create Pods directly; a Deployment or similar controller manages them for you.
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: myapp:1.4
ports:
- containerPort: 8080A Deployment manages a set of identical Pods for a stateless app. You declare how many replicas and which image; the Deployment creates a ReplicaSet that keeps that many Pods running, replacing any that die.
Deployments own rolling updates and rollbacks: change the image and Kubernetes gradually replaces old Pods with new ones, and you can undo to the previous revision if something breaks. It's the object you'll use most.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: myapp:1.4Key point: Know that a Deployment manages a ReplicaSet which manages Pods. That three-layer chain is a frequent follow-up.
A Service gives a stable network identity (a fixed name and virtual IP) to a set of Pods that come and go. Since Pods are ephemeral and their IPs change, other components talk to the Service, and Kubernetes load-balances across the healthy Pods behind it.
The common types: ClusterIP (internal only, the default), NodePort (exposes a port on every node), and LoadBalancer (provisions an external load balancer from the cloud). A Service selects its Pods by label.
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 8080
type: ClusterIPBoth inject configuration into Pods so you don't bake it into the image, keeping the same image usable across environments. A ConfigMap holds non-sensitive config (feature flags, URLs, tuning values). A Secret holds sensitive data (passwords, tokens, keys).
The honest caveat: a stock Secret is only base64-encoded, not encrypted, in etcd by default. Real security comes from enabling encryption at rest, tight RBAC, and ideally sourcing secrets from an external manager. Never store a password in a ConfigMap.
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
LOG_LEVEL: info
FEATURE_NEW_UI: "true"
---
apiVersion: v1
kind: Secret
metadata:
name: db-creds
type: Opaque
stringData:
DB_PASSWORD: change-me-in-a-real-managerKey point: Don't claim Secrets are encrypted by default. Saying 'base64, not encryption, unless you enable encryption at rest' is the accurate answer the question needs.
An operation is idempotent if running it many times has the same effect as running it once. Applying a Kubernetes manifest or a Terraform config is idempotent: it converges to the declared state whether the resource already exists or not.
It matters because automation reruns constantly (retries, drift correction, repeated deploys). Idempotent operations are safe to rerun, which is the whole reason declarative tools like Terraform and Kubernetes are reliable, versus a fragile imperative script that breaks on the second run.
Imperative means you list the steps: create this, then run that command, then patch this. Declarative means you describe the desired end state and let the tool figure out how to reach it. Kubernetes manifests and Terraform configs are declarative.
Declarative wins for infrastructure because it's idempotent and self-documenting: the config is the source of truth, and the system reconciles reality toward it continuously. Imperative scripts drift and are hard to reason about after the fact.
# imperative: a sequence of commands
kubectl run web --image=myapp:1.4
kubectl scale deployment web --replicas=3
# declarative: describe the state, apply it
kubectl apply -f deployment.yamlKey point: declarative maps to idempotency and 'the file is the source of truth'. That framing shows you understand why IaC tools are declarative.
Environment parity means dev, staging, and production run the same OS, dependencies, and config so 'works on my machine' problems disappear. Containers and infrastructure as code are the main tools that make parity practical.
When environments drift, bugs hide until production, and staging stops being a trustworthy signal. Parity is why teams containerize apps and provision every environment from the same code, changing only the config values, never the recipe.
For candidates with working experience: Kubernetes operations, infrastructure as code, observability, and the judgment questions that separate tool users from operators.
A multi-stage build uses multiple FROM stages in one Dockerfile: an early stage compiles or bundles the app with all the build tools, and a final slim stage copies only the finished artifact. The heavy build dependencies never reach the shipped image.
The payoff is smaller, safer images: a final image with just the runtime and your binary is smaller to pull, faster to start, and has a smaller attack surface because compilers and dev packages aren't present.
# build stage: has the full toolchain
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# final stage: only the runtime and the built app
FROM node:20-slim
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]Key point: The wins: smaller image and smaller attack surface. Security-minded answers score higher than 'it makes the image smaller'.
A Service exposes Pods, usually inside the cluster or via a per-service cloud load balancer. An Ingress sits in front as an HTTP/HTTPS router: it takes one external entry point and routes by hostname and path to different Services, and it terminates TLS.
So you'd use one Ingress with rules like api.example.com to the api Service and app.example.com to the frontend Service, instead of provisioning a separate cloud load balancer per Service. An Ingress needs an ingress controller (nginx, Traefik, a cloud one) actually running to do the work.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80Key point: Mention that an Ingress resource does nothing without an ingress controller running. Forgetting that is a classic gotcha interviewers probe.
Watch a deeper explanation
Video: Kubernetes Crash Course for Absolute Beginners [NEW] (TechWorld with Nana, YouTube)
Probes tell Kubernetes about container health. A liveness probe checks whether the container is alive; if it fails, Kubernetes restarts the container. A readiness probe checks whether the container can serve traffic; if it fails, the Pod is pulled from Service endpoints but not restarted. A startup probe protects slow-booting apps by holding off the other probes until the app has finished starting.
Getting these right prevents two classic failures: sending traffic to a Pod that isn't ready yet, and restart loops on an app that's just slow to warm up. Readiness gates traffic; liveness gates restarts.
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5Key point: The distinction 'readiness gates traffic, liveness gates restarts' is the exact answer. Mixing them up is the most common mistake here.
A rolling update replaces old Pods with new ones gradually, controlled by maxSurge (how many extra Pods above the target during the update) and maxUnavailable (how many can be down). This keeps the app serving throughout, and readiness probes ensure traffic only goes to Pods that are actually up.
Every change creates a new revision, so a rollback is one command that returns to the previous ReplicaSet. If a bad image ships, kubectl rollout undo restores the last good version without a rebuild.
# trigger a rolling update
kubectl set image deployment/web web=myapp:1.5
# watch it
kubectl rollout status deployment/web
# something broke: roll back to the previous revision
kubectl rollout undo deployment/webKey point: Knowing maxSurge and maxUnavailable by name, and that rollback uses stored revisions, signals real Kubernetes operations experience.
The Horizontal Pod Autoscaler (HPA) adds or removes Pod replicas based on observed metrics, most commonly CPU or memory utilization against a target, or custom metrics like requests per second. It reads metrics, compares to the target, and adjusts the replica count within your min and max bounds.
It needs a metrics source (the metrics-server for CPU/memory) and sensible resource requests set on the Pods, because CPU utilization is measured against the request. Without requests, the HPA has no baseline and can't scale correctly.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70Key point: Mention that CPU-based HPA depends on resource requests being set. That dependency is a favorite follow-up and separates users from operators.
Kubernetes is overkill when the operational cost outweighs the benefit: a single container, a small app with steady traffic, an early-stage product, or a team without anyone to run the cluster. It brings real ongoing burden: upgrades, networking, RBAC, monitoring the control plane.
The alternatives worth naming: a managed platform (Cloud Run, App Runner, Fly, Render, Heroku-style PaaS) that hosts containers without you running an orchestrator, or plain VMs with a load balancer for something simple. Reach for Kubernetes when you have many services that genuinely need self-healing, autoscaling, and zero-downtime rollouts across a fleet.
Key point: Being willing to say 'don't use Kubernetes here' is a strong production signal. Interviewers ask this to weed out cargo-culting.
Terraform state is a file that maps your configuration to the real resources it manages. It records what Terraform created and their current attributes, so on the next run it knows what already exists and what a change should add, modify, or destroy.
For any team, state must live remotely (an S3 bucket, Terraform Cloud, a GCS bucket) with locking enabled, not on one laptop. Remote state is shared, and locking stops two people from applying at once and corrupting it. The state can also hold secrets, so it must be encrypted and access-controlled.
# configure remote state with locking (backend block)
# then:
terraform init # sets up the backend
terraform plan # reads state, shows the diff
terraform apply # takes a lock, updates stateKey point: Say 'remote state with locking' and 'state can contain secrets'. Both come up as follow-ups and show you've run Terraform on a real team.
Watch a deeper explanation
Video: GitHub Actions Tutorial - Basic Concepts and CI/CD Pipeline with Docker (TechWorld with Nana, YouTube)
terraform plan is a dry run: it compares your configuration against state and the real infrastructure and prints exactly what it would create, change, or destroy, without touching anything. terraform apply executes that plan.
The safe workflow reviews the plan (ideally in a pull request, with the plan output posted as a comment) before applying, because plan is where you catch a change that would accidentally destroy a database. In CI, you generate a saved plan and apply that exact plan, so what's reviewed is what runs.
# review-then-apply the exact plan
terraform plan -out=tfplan
# ... a human reviews the diff ...
terraform apply tfplanKey point: Emphasize reviewing the plan before applying, especially for destroy actions. That discipline is what the question screens for.
Drift is when the real infrastructure no longer matches the Terraform config, usually because someone changed something by hand in the console, or an external process modified a resource. Terraform detects it: the next plan shows changes you didn't make in code.
You handle it by deciding whether the change should be kept (import it into config) or reverted (apply to bring reality back to code). The prevention is discipline: no manual console changes, and drift-detection runs in CI that flag when something diverged so it doesn't silently pile up.
Key point: Frame the fix as 'reconcile the code with reality, then prevent manual changes'. Blaming the console without a prevention plan indicates junior.
Configuration management tools (Ansible, Chef, Puppet, Salt) bring existing servers to a desired state: install packages, write config files, manage services. Infrastructure as code (Terraform, CloudFormation) provisions the infrastructure itself: the VMs, networks, and load balancers.
They're complementary: IaC creates the machine, config management configures what's on it, though in a container world much of the config-management job moves into the image build. Modern immutable-infrastructure setups often replace in-place config management with rebuilding images and replacing instances.
| Concern | Infrastructure as code | Config management |
|---|---|---|
| Creates the machine/network | Yes (Terraform, CloudFormation) | No |
| Configures software on a host | Not its job | Yes (Ansible, Puppet) |
| Model | Declarative, provision resources | Converge a host to a state |
Key point: Add that containers push much of config management into the image build. That nuance shows you understand the modern immutable-infrastructure shift.
Monitoring is watching known signals and alerting when they cross thresholds: is CPU high, is the error rate up, is the service responding. You define the questions in advance. Observability is being able to ask new questions about a system's internal state from its outputs, to debug problems you didn't anticipate.
Monitoring tells you something is wrong; observability helps you find out why, especially for novel failures in a distributed system. The three pillars, metrics, logs, and traces, are the raw material observability is built on.
Key point: The crisp line is 'monitoring answers known questions, observability lets you ask new ones'. the key signal is exactly that framing.
Metrics are numeric measurements aggregated over time (request rate, error percentage, latency percentiles): cheap to store, great for dashboards and alerts, but they don't explain a single request. Logs are timestamped discrete events with detail: great for the specifics of what happened, but noisy and costly at scale.
Traces follow one request across every service it touches, showing where time went and where it failed: the key tool for debugging latency and errors in a distributed system. You reach for metrics to notice a problem, traces to localize it, and logs to see the exact detail.
Key point: Give the workflow: metrics to detect, traces to localize, logs for detail. Showing how they combine beats defining each in isolation.
An SLI (Service Level Indicator) is a measured signal of user-facing health, like the percentage of requests served successfully under 300ms. An SLO (Service Level Objective) is the target for that indicator, say 99.9% over 30 days. An error budget is what's left over: 100% minus the SLO, the amount of failure you're allowed to spend.
The point of the error budget is it turns reliability into a shared decision. Budget remaining means you can ship faster and take risk; budget exhausted means you slow down and focus on stability. It replaces arguments about 'is it reliable enough' with a number both dev and ops agreed on.
Key point: Explaining that the error budget governs release velocity is the production insight. Reciting the acronyms without that connection reads shallow.
A good alert is actionable, urgent, and tied to user impact: it fires when a human needs to do something now, points at the problem, and links to a runbook. Alerting on symptoms (users are seeing errors) beats alerting on causes (CPU is 90%), because high CPU may be harmless while a spiking error rate always matters.
Alert fatigue comes from too many noisy, non-actionable pages; people start ignoring them and miss the real one. Fixes: alert on SLO burn rate rather than every blip, delete or tune alerts that never lead to action, and route low-urgency issues to a ticket instead of a page.
Key point: Say 'alert on symptoms and user impact, not raw resource metrics'. That principle is what distinguishes a mature alerting answer.
A request is what the scheduler reserves for a container: it's used to decide which node the Pod fits on, and it's the guaranteed baseline. A limit is the hard ceiling: exceed a CPU limit and the container is throttled; exceed a memory limit and the container is killed (OOMKilled).
Setting them well is what keeps a cluster stable. Too low and Pods get evicted or throttled under load; too high and you waste capacity and pack fewer Pods per node. Requests also anchor the Horizontal Pod Autoscaler's CPU math, so getting them wrong quietly breaks autoscaling too.
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"Key point: Knowing that hitting a memory limit means OOMKilled while a CPU limit means throttling is the precise distinction the key signal is.
kubectl get pods to see the status (CrashLoopBackOff, ImagePullBackOff, Pending each point somewhere different), then kubectl describe pod for the events at the bottom, which usually The cause: failed image pull, failed scheduling, or a failing probe comes first. For a crashing container, kubectl logs with --previous shows the logs from the crashed instance, not the restarting one.
Pending usually means the scheduler can't place the Pod (insufficient resources, a node selector or taint), ImagePullBackOff means a bad image name or missing registry credentials, and CrashLoopBackOff means the container starts then exits, so read its logs. For deeper poking, kubectl exec into a running Pod or attach an ephemeral debug container.
kubectl get pods # see the status
kubectl describe pod web-abc123 # events explain why
kubectl logs web-abc123 --previous # logs from the crashed instance
kubectl exec -it web-abc123 -- sh # poke around insideKey point: Mapping each status (Pending, ImagePullBackOff, CrashLoopBackOff) to its usual cause is what proves you've actually debugged clusters, not just deployed to them.
First detect and declare: acknowledge the alert, open a channel, and assign a clear incident commander so communication stays coordinated. Then mitigate before diagnosing fully, roll back the recent deploy, scale out, or fail over to restore service, because stopping user pain comes before understanding the cause.
Once service is stable, resolve the root cause properly, then run a blameless postmortem: a written timeline, contributing factors, and concrete action items. The structure that works well is detect, mitigate, resolve, learn, with 'mitigate before deep diagnosis' as the key instinct.
Incident response flow
Mitigate before you fully diagnose: restoring service is the priority, and the deep root-cause work happens after users are safe.
Key point: Lead with 'mitigate first, diagnose later'. Candidates who want to fully root-cause before restoring service usually fail this one.
A blameless postmortem is a written analysis after an incident: a factual timeline, what contributed, what went well, and specific action items with owners. Blameless means it focuses on the systems and processes that let the failure happen, not on punishing the person who pushed the button.
It's blameless because blame kills honesty. If people fear punishment, they hide details and the real causes never surface, so the same incident recurs. Treating human error as a symptom of a system that allowed it, and fixing the system, is what actually improves reliability.
Key point: The causal reasoning: blame suppresses the honest detail you need to prevent recurrence. That's the real 'why', not just 'be nice'.
Rolling replaces old Pods with new ones gradually with no downtime, but there's a window where both versions serve traffic and a bad version reaches everyone as it ramps. Blue-green runs two full environments and flips all traffic from old (blue) to new (green) at once, giving instant rollback but doubling infrastructure during the switch.
Canary sends a small slice of traffic to the new version, watches metrics, and ramps up only if it's healthy, giving the smallest blast radius at the cost of more automation and observability to run it. Feature flags are the finer-grained cousin: ship code dark and toggle it on for a subset of users independent of deploys.
Deployment strategies: rollout risk vs speed
Lower risk means a smaller blast radius if the new version is bad. Recreate is fastest to reason about but has downtime and full exposure.
| Strategy | Downtime | Rollback speed | Blast radius if bad |
|---|---|---|---|
| Recreate | Yes | Redeploy old | Everyone at once |
| Rolling | No | Roll out previous | Grows as it ramps |
| Blue-green | No | Instant (flip back) | Everyone at switch |
| Canary | No | Stop the ramp | Small slice first |
Key point: Bring up feature flags as a complement to these strategies. It shows you separate 'deploy code' from 'release a feature', a mature distinction.
A feature flag is a runtime toggle that turns a code path on or off without redeploying. You ship the code dark (merged and deployed but off), then enable it for a percentage of users, a specific segment, or everyone, and turn it off instantly if something breaks.
They decouple deploy from release: the risky moment becomes flipping a flag, not shipping code, which pairs perfectly with trunk-based development and continuous deployment. The cost is flag hygiene, stale flags pile up as technical debt and must be cleaned out once a feature is fully rolled out.
Key point: Mention flag cleanup as real debt. Acknowledging the downside, not just the upside, indicates someone who's actually lived with flags.
Secrets never live in source control or in the image. They come from a dedicated secrets manager (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault) and are injected at runtime as environment variables or mounted files. Access is scoped per service via short-lived credentials or workload identity, not a shared master key.
In the pipeline itself, CI secrets are stored in the platform's encrypted secret store and exposed only to the jobs that need them. Good practice adds rotation, an audit log of who accessed what, and scanning to catch secrets accidentally committed. The failure to avoid is a plaintext token in a repo or a CI log.
Key point: Cover both runtime secrets and CI secrets, plus rotation and scanning. A one-dimensional 'use Vault' answer misses what the question is really testing.
advanced rounds probe architecture, security, and production scars. Expect every answer here to draw a follow-up about trade-offs and failure modes.
Treat the pipeline as production, because it has the keys to production. Lock down least-privilege credentials (short-lived, per-job, via OIDC/workload identity rather than long-lived keys), pin third-party actions and base images to digests so a compromised tag can't inject code, and require reviews plus branch protection so no one can push straight to a deploy branch.
Then add scanning as gates: dependency and container scanning, secret scanning to catch committed credentials, and SBOM generation for supply-chain visibility. Sign artifacts and verify signatures at deploy so only trusted builds reach production. The threat model to name is supply-chain: your build system is a high-value target.
Key point: Frame it as 'the pipeline is production': it holds deploy credentials. That mindset, plus supply-chain awareness, is the senior-level answer.
Supply-chain security is about trusting everything that goes into your build: base images, dependencies, build tools, and the pipeline itself. The attacks are real, a poisoned dependency or a compromised build step ships malicious code to everyone downstream, so you can't only secure your own source.
Concrete measures: pin dependencies and images to immutable digests, generate an SBOM so you know exactly what's inside a build, scan for known vulnerabilities, sign artifacts and verify signatures before deploy (Sigstore/cosine-style), and follow SLSA provenance so you can prove how an artifact was built. Defense in depth, because any single control can be bypassed.
Key point: Naming SBOM, artifact signing, and SLSA provenance signals current supply-chain literacy, which is a hot topic in senior DevOps interviews.
Scale horizontally by default: stateless services behind a load balancer so you add identical instances under load, with autoscaling driven by a real signal (CPU, requests per second, queue depth). Push state out of the instances into shared stores (databases, caches, object storage) so any instance can serve any request and instances stay disposable.
Load balancing spreads traffic across healthy instances and removes unhealthy ones via health checks; layer it (an external load balancer to the cluster edge, then service-level balancing inside). The parts that don't scale by adding instances, the database, are where you invest: read replicas, connection pooling, caching, and eventually sharding.
Key point: Say 'make instances stateless and disposable' early. The database being the real bottleneck is the follow-up interviewers love to push on.
Cache at the layers that hurt: a CDN for static assets and cacheable responses at the edge, an in-memory cache (Redis, Memcached) for hot database reads and computed results, and application-level caching for expensive work. Each layer removes load from the slower thing behind it.
The hard parts are invalidation and staleness: deciding when cached data is wrong and evicting it, choosing TTLs, and handling the thundering herd when a popular key expires and every request stampedes the database at once. Patterns like request coalescing, staggered TTLs, and stale-while-revalidate exist precisely because 'just cache it' hides real complexity.
Key point: cache invalidation and the thundering-herd problem matters.
Use the expand-and-contract (parallel change) pattern so the schema is compatible with both the old and new code at every step. Expand: add the new column or table without removing the old, deploy code that writes to both and reads the old, backfill existing data, then switch reads to the new. Contract: once nothing uses the old schema, remove it in a later deploy.
The rule is never make a breaking change in a single step while old code is still running. Avoid long-locking operations on big tables, use online-migration tooling, and keep each step independently deployable and reversible. The whole point is decoupling the schema change from the code change so neither one requires downtime.
Key point: Naming 'expand-and-contract' and 'backwards-compatible at every step' is the answer. Proposing a single ALTER while old pods run fails this question.
Stateful sets exist for it: StatefulSets give stable network identities and stable per-Pod storage (PersistentVolumeClaims) so a database Pod keeps its identity and data across restarts and reschedules. You pair that with an operator that understands the specific database's backup, failover, and scaling.
The honest senior take is whether you should: managed database services (RDS, Cloud SQL) offload the hardest parts, backups, failover, patching, so many teams run stateless apps on Kubernetes and keep the database managed outside it. Running your own stateful database on Kubernetes is doable with a mature operator, but it's a real commitment, not a default.
Key point: Answering 'you can, but often shouldn't' with the managed-DB reasoning is the mature take. Enthusiastically running Postgres by hand on K8s is a red flag.
GitOps makes a Git repository the single source of truth for the desired state of your infrastructure and deployments. A controller in the cluster (Argo CD, Flux) continuously compares the live state to what's declared in Git and reconciles any difference, so you deploy by merging a pull request, not by running kubectl.
What it changes: every change is reviewed, versioned, and auditable in Git; rollback is a git revert; and drift is auto-corrected because the controller keeps pulling reality back to the declared state. The trade-off is another moving piece to operate and the discipline that nothing bypasses Git, or the reconciliation loop just reverts your manual fix.
Key point: Mention that manual changes get reverted by the reconciler. That both explains GitOps's power and shows you understand its operational implication.
A service mesh (Istio, Linkerd) puts a sidecar proxy next to each service to handle service-to-service concerns uniformly: mutual TLS, retries, timeouts, traffic splitting for canaries, and rich telemetry, without changing application code. The control plane configures all the proxies centrally.
It's worth it when you have many services and need consistent security (mTLS everywhere), fine-grained traffic control, and per-hop observability that you don't want to reimplement in every service. It's overkill for a handful of services, where the added latency, resource overhead, and operational complexity outweigh the benefit. Linkerd is the lighter option when you want mesh basics without Istio's weight.
Key point: As with Kubernetes, being able to say 'not worth it yet' for a small system is the signal. Reflexively adding Istio is a maturity red flag.
the requirement, not the architecture: what RTO and RPO does the business actually need, because true active-active multi-region is expensive and complex comes first. Common tiers are multi-AZ within one region (cheap, handles most failures), warm standby in a second region (failover in minutes), and active-active (highest availability, hardest because of data replication and conflict handling).
The hard part is always the data: cross-region replication has latency and consistency trade-offs, and split-brain during a partition is a genuine risk. Route traffic with health-checked DNS or global load balancing, replicate state deliberately, and rehearse failover regularly, because an untested failover plan usually fails when it's needed.
Key point: Anchor the answer in RTO/RPO and 'the data is the hard part'. Jumping straight to active-active without the cost discussion indicates inexperienced.
Measure first: attribute spend to teams and services with tagging so you know where the money goes, because cutting blind hurts reliability. Then work the big levers: right-size over-provisioned instances, use autoscaling to match capacity to demand, buy committed-use or savings plans for steady baseline load, and use spot/preemptible instances for fault-tolerant workloads.
The reliability guardrail matters: don't cut redundancy that protects availability, and don't chase savings that add so much operational risk that one incident erases them. The best wins are usually turning off waste (idle resources, oversized storage, forgotten environments) rather than degrading production.
Key point: Lead with 'attribute spend before cutting'. Framing cost as an engineering discipline with a reliability guardrail beats a list of discounts.
Chaos engineering deliberately injects failure (kill instances, add latency, drop a dependency, simulate a zone outage) to verify the system recovers as designed, before a real failure proves it doesn't. It turns 'we think we're resilient' into evidence.
Safely means: form a hypothesis about steady-state behavior, start in a non-production or low-traffic environment, keep the blast radius small, always have an abort switch, and run during business hours when people are watching. Game days, scheduled controlled experiments, are the common on-ramp. The goal is learning, not breaking things for its own sake.
Key point: Stress the safety controls: hypothesis, small blast radius, abort switch. Chaos without guardrails is recklessness, and interviewers check that you know the difference.
Immutable infrastructure means you never modify a running server in place. To change anything, you build a new image (a new AMI, a new container image) and replace the instances, rather than SSHing in to patch. Containers make this the default: a new deploy is new containers, not edited ones.
The wins: no configuration drift, because every instance came from the same image; trivially reproducible environments; and clean rollbacks, since the previous image is still there to redeploy. The cost is you must rebuild and redeploy for every change, including small ones, which is exactly the trade the CI/CD pipeline is built to make cheap.
Key point: immutability connects to 'no drift' and 'clean rollback'. Those two outcomes are why the approach won, and naming them shows real understanding.
The core model is flat: every Pod gets its own IP, and every Pod can reach every other Pod without NAT. A CNI plugin (Calico, Cilium, the cloud's own) implements that pod network. Services provide stable virtual IPs and load-balance across Pod IPs, DNS (CoreDNS) resolves Service names, and Ingress or a gateway handles external HTTP routing.
Security comes from NetworkPolicies, which are default-allow until you add them: they restrict which Pods can talk to which, giving you segmentation. At senior level it helps to know where the abstraction leaks: kube-proxy or eBPF dataplane behavior, DNS caching issues, and MTU mismatches are common real-world networking headaches.
Key point: Knowing NetworkPolicies are default-allow, and naming a real failure mode like DNS or MTU issues, separates someone who's debugged cluster networking from someone who's only read about it.
Emit structured logs (JSON with consistent fields) so they're queryable, include a correlation or trace ID on every line so you can follow one request across services, and log at the right level, because logging everything at full volume is expensive and buries the signal. Ship logs off the host with a collector (Fluent Bit, Vector) to a central store (Loki, Elasticsearch, a cloud log service).
At scale, cost forces choices: sample high-volume debug logs, set retention by value (keep security logs longer than debug noise), and lean on metrics and traces for the things logs are bad at. The mistake to avoid is treating logs as the answer to everything; they're one pillar, and unstructured firehose logging is where log bills and outages both come from.
Key point: Push structured logs plus a correlation ID, and cost-driven sampling/retention. It shows you've owned a log pipeline, not just written console.log.
Distributed tracing follows a single request across every service it touches. Each service creates spans (units of work) tagged with a shared trace ID that propagates through headers, and the spans assemble into one waterfall showing where time was spent and where the request failed. OpenTelemetry is the vendor-neutral standard for instrumenting it.
It solves the core microservices debugging problem: when a request is slow or broken, metrics tell you something's wrong and logs give you scattered detail, but only a trace shows the actual path and which hop, the third database call, a slow downstream API, caused it. Context propagation across service boundaries is the part teams most often get subtly wrong.
Key point: The OpenTelemetry and 'context propagation across service boundaries'. Both signal you've instrumented tracing rather than just consumed a dashboard.
Clarify first: how many services, how often do they ship, what's the risk tolerance, and is there a compliance gate. Then lay out the flow: on merge to main, CI builds and tests each changed service, produces an immutable container image tagged by commit, scans it, and pushes it to a registry. A GitOps controller (Argo CD or Flux) watches a config repo where the deploy manifests live and reconciles the cluster to match.
For safety, promote the same image through environments (staging, then production), deploy with a progressive strategy (canary or blue-green) gated on health metrics, and make rollback a git revert. Wrap it with observability (metrics, logs, traces on every service), least-privilege pipeline credentials, and secrets from a manager. The structure that scores is clarify, build-once, GitOps promote, progressive rollout, observe and roll back, each step justified by the requirements you asked for.
# sketch of the CI trigger (GitHub Actions)
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t ghcr.io/acme/web:${{ github.sha }} .
- run: docker push ghcr.io/acme/web:${{ github.sha }}
# a separate job / GitOps commit updates the deploy manifest
# then Argo CD reconciles the cluster to the new imageKey point: Open by asking clarifying questions before designing. Jumping straight to a tool list, without requirements, is the most common way to underperform on a design prompt.
Containers package an app with its dependencies and share the host kernel, so they start in milliseconds and pack densely. Virtual machines emulate a full machine with their own kernel, so they isolate harder but cost more to run. Kubernetes orchestrates containers across many machines: it earns its complexity when you're running many services that need self-healing, autoscaling, and zero-downtime rollouts, and it's overkill for a single container or a small app that a managed platform can host. Knowing where each option fits, and saying the trade-off out loud, is itself an interview signal.
| Option | Isolation | Best at | Watch out for |
|---|---|---|---|
| Container | Process-level, shared kernel | Fast startup, dense packing, consistent envs | Weaker isolation than a VM; kernel is shared |
| Virtual machine | Full OS, own kernel | Strong isolation, mixed OS, legacy workloads | Slow boot, heavier resource use |
| Kubernetes | Orchestrates containers across nodes | Many services, self-healing, autoscaling, rollouts | Real operational cost; overkill for small apps |
| Managed platform (PaaS) | Container behind a managed layer | Small teams shipping one or few services fast | Less control, per-vendor limits |
Prepare in layers, and trade-offs out loud is the explanation path. Most DevOps rounds move from concept questions to a hands-on or whiteboard task to a scenario discussion, so rehearse each stage rather than only reading answers.
The typical DevOps interview flow
Earlier rounds increasingly run as AI-driven technical interviews. The concepts on this page are what gets 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. DevOps rounds pull in real pipeline, container, and Kubernetes scenarios, and these questions reflect what actually gets asked and evaluated inside the interviews we host for 5,000+ HR teams.
See how the AI Coding Interviewer works