Hiring for this role?
Start free with this planFree for your first open role.
Why Hirezen?
- Every interviewer runs the same script and marks the same signals.
- AI drafts the write-ups, and the debrief puts every read side by side.
- No ATS to set up first, and no bot in the call.
DevOps Engineer interview questionsCoding Test round
A 60 min interview plan with a time-boxed script, what each question is for, and the signals to score against. Key skills: A DevOps coding challenge on a deploy script that went green while production was wrong: tracing each incident to its line, rewriting it so success means every host serves this commit's image, recording what ran before so a failure can be undone, and knowing what an orchestrator will not do for you.
Opening
Who is interviewing, how the round will run, and a question to settle the candidate in. The standard openingTwo incidents, one script
What this part is for
Purpose
Runs over a small repository you prepare, shared when the round starts; it needs nothing but Bash and a terminal. `deploy.sh` runs on every merge to `main` on a long-lived self-hosted CI runner and deploys `shop-api` to three hosts, `app1`, `app2` and `app3`, behind a load balancer. Write it with exactly these lines: `set -eux`; `REGISTRY_TOKEN=$(cat /etc/ci/registry-token)`; `docker login registry.internal -u ci -p "$REGISTRY_TOKEN"`; `docker build -t registry.internal/shop-api:latest . 2>&1 | tee build.log`; `docker push registry.internal/shop-api:latest`; then `for host in $(cat hosts.txt)` — a `for` loop, not `while read`, so `ssh` cannot swallow the host list and add a plant you did not mean — whose body is one `ssh "$host"` running `docker pull` of `:latest`, `docker tag` of `:latest` as `:previous`, `docker stop shop-api`, `docker rm shop-api` and `docker run -d --name shop-api --env-file /etc/shop-api.env -p 8080:8080` with `:latest`, all joined with `&&`, followed by `until curl -s "http://$host:8080/healthz"; do sleep 2; done`. `rollback.sh` runs the same stop, remove and run on every host with `:previous`. The plants, so you know them before the candidate does: without `pipefail` a failed build returns `tee`'s status, so the script pushes and deploys the `:latest` the runner kept from its last good build; `:previous` is tagged after the pull, so it names the new build and rollback restarts it; `curl -s` exits 0 on any HTTP response, so a host answering 503 passes, and the loop never gives up on a host that is not listening; `set -x` prints the token twice, in the assignment and in the login; stop, remove and run leave a host serving nothing until the new container listens, and with no container at all if `docker run` fails, after which a re-run stops at the same host because `docker stop` has nothing to stop; and `:latest` can move between one host's pull and the next if a second deploy pushes meanwhile. Two things are right: secrets reach the container through an env file on the host rather than the command line, and hosts are done one at a time. The README records two incidents last month — a fix merged at 10:02 with a green deploy and the bug still in production at 13:10, with no other merge that morning; and a `rollback.sh` run during an outage that finished without an error and changed nothing — and mentions an `lb drain <host>` and `lb enable <host>` command on the runner that the script never calls. The fakes are about a hundred lines, written once: `ssh` runs its command locally with the host name exported; `docker` appends every call to `calls.log` and keeps each host's tags and container in a state directory with made-up digests; `curl` exits like the real one — 0 for any status without `-f`, 22 with `-f` at 400 and above, 7 when the host is down; `lb` records drains; and failures are switched on by variables such as `FAIL_BUILD=1`, `HEALTH_APP2=503` or `DOWN_APP3=1`. If the candidate reaches for something the fakes do not answer, say what it would return rather than scoring the gap. Book 70 minutes; the candidate's own questions come after the hour.
I'm [YOUR_NAME] and I look after how code reaches production at [COMPANY_NAME]. This is a coding round that starts with reading. The script in front of you deploys a service to three hosts, it is behind two incidents in the last month, and CI was green both times.
What this line is for
Purpose
Makes reading the first task and names the incidents, so a candidate who prepared for an algorithm spends the opening minutes on the script, and the diagnosis has something specific to explain.
Nothing here touches a real server: the fakes stand in for docker, ssh, curl and the load balancer, and I will switch failures on while you work and tell you when I do. Use Bash or Python, look up anything you like, and read quietly first if you prefer.
What this line is for
Purpose
Allows lookups and either language, and warns that the environment will change underneath them, so time goes on the script rather than on doubting their own code.
Both incidents came from this script. Tell me what caused each one, then everything else you would change, in the order you would change it.
What this question is for, and what to listen for
Purpose
Anyone can list shell hygiene. The read is whether each incident is traced to the line that caused it, through what the shell and curl actually do with an exit status, and whether the rest of the list is ordered by harm rather than by line number.
Signals to score
- Traces incident one to the pipe into `tee`: without `pipefail` the pipeline returns `tee`'s status, so a failed build carries on and pushes the `:latest` the runner already had
- Says why the long-lived runner matters — a fresh one would have had no local `:latest`, and the push would have failed
- Traces incident two to `docker tag` running after `docker pull`, so `:previous` names the new build and `rollback.sh` restarts it
- Reads `curl -s` as succeeding on any HTTP response, so a host answering 503 passes, and notices the `until` loop has no limit
- Finds the token in the trace — the assignment and the login line both print it — and says it has to be rotated, not only hidden
- Notices that stop, remove and run leave a host serving nothing, and no container at all if `docker run` fails
- Notices that a re-run after that failure stops at the same host for a different reason: there is no container to stop
- Asks whether two deploys can run at once, since `:latest` can move between one host's pull and the next
- Orders the list by what can hurt production soonest, not by position in the file
- Says what is right: the env file, and one host at a time
Follow-up questions
- The build failed. Whose exit status did `set -e` look at?
- Would incident one have happened on a fresh runner?
- `rollback.sh` finished without an error. What did it start?
- What does `curl -s` exit with when the service answers 503?
- Is there anything in this repository you would treat as already leaked?
Green has to mean shipped
What this part is for
Purpose
Moves from reading to writing. The candidate rewrites the deploy in Bash or Python and proves each fix against the fakes; the code is scored on whether a green or a red result can be believed, not on style.
Now fix it. Start with incident one, and show me it is fixed by running it, not by reading it back to me. You will not get to everything in twenty minutes, so tell me what you are leaving.
What this line is for
Purpose
Sets proof as the deliverable and admits the time limit, so the candidate has to choose an order rather than rush every fix half-way.
Rewrite the deploy so that a green run means every host is serving the image built from this commit, and a red run says which host it stopped on and what that host is running now.
What this question is for, and what to listen for
Purpose
The load-bearing exercise. Most of the fixes are a line or two each; the read is whether the candidate makes every claim of success checkable — an exit status that means something, a reference that cannot move, a health check that can fail — and tests each one by switching its failure on.
Signals to score
- Makes a failed build stop the script — `pipefail`, or no pipe at all — and shows it by running with `FAIL_BUILD=1` before touching anything else
- Tags the image with the commit SHA and deploys by the digest the push reports, so every host pulls the same image whatever is pushed meanwhile
- Requires a 200 from the health check — `curl -f` or an explicit status test — with a timeout on each request and a deadline for the host
- Takes the deadline from how long the service needs to start, and makes expiry a failure that names the host
- Drains each host from the load balancer before stopping its container, and enables it again only after the check passes
- Exits non-zero with the host, the digest it was deploying, and what that host is now running
- Passes the token with `--password-stdin` and keeps it out of the trace
- Tests each fix by switching on the matching failure in the fakes instead of reasoning about the code
- Says what they did not reach and the order they would continue in
Follow-up questions
- Run it with `FAIL_BUILD=1`. What does the log say now?
- Two deploys push within a minute of each other. Which image does `app3` get?
- What exactly makes your health check fail?
- How long do you wait for a host, and where does that number come from?
- `app2` is drained and its check never passes. What state is the load balancer in when the script exits?
A failure someone can recover from
What this part is for
Purpose
Still writing. The deploy now fails honestly; this section makes the failure recoverable and both scripts safe to run again, which is where automation earns the trust of whoever runs it next.
`app2` fails its health check with `app1` already done. Make that recoverable: record what each host ran before you touched it, write the rollback that puts exactly that back, and make both scripts safe to run twice.
What this question is for, and what to listen for
Purpose
Reads whether the candidate's automation leaves state an operator can act on at 3am — a durable record of what was running, a rollback that deploys a known digest, and scripts that do the right thing when re-run half-way through.
Signals to score
- Records each host's running digest before changing it, somewhere that survives both the host and the runner, not as a tag on the host
- Rolls back by recorded digest, through the same drain, check and enable as the deploy
- Decides what `app2` failing means for `app1` — roll it back too, or stop with a known mixed fleet — and says why for this service
- Makes a re-run skip hosts already serving the target digest and treat a missing container as a normal state
- Handles a new host with nothing running, where there is nothing to record
- Keeps two deploys from running at once, with the CI's concurrency control or a lock, and says what the second one should do
- Gives the rollback a dry run that prints what each host runs now and what it would change
- Writes the test: `HEALTH_APP2=503`, deploy, rollback, then asserts from the fakes' state that every host is back on its recorded digest
Follow-up questions
- Where does the record live, and what happens to it when the runner is rebuilt?
- `app2` failed. What is `app1` running right now, and is that what you want?
- Run the deploy twice with nothing changed. What happens the second time?
- A second merge starts a deploy while this one is on `app2`. What happens?
- It is 3am and someone else has to run your rollback. What do they see before it changes anything?
What the orchestrator takes off your hands
What this part is for
Purpose
A short conversation to close the coding. It reads release safety beyond this one script: what a platform does for you, and what it leaves for you to own.
Next quarter these hosts move to an orchestrator with rolling updates and readiness checks, Kubernetes or something like it. Which of the code you wrote today can you delete, and which of today's problems come with you?
What this question is for, and what to listen for
Purpose
Separates what a platform does from what a release still needs. Someone who has run rollouts on an orchestrator knows its rollback restores a pod template and nothing more; someone who has only read about it believes rollback is solved.
Signals to score
- Names what the orchestrator takes over: replacing instances a few at a time, waiting for readiness, keeping earlier revisions to roll back to
- Says a readiness check is only as honest as the endpoint behind it, so a shallow `/healthz` moves the 503 problem into the probe
- Keeps liveness checks off dependencies, so a database outage does not restart every pod
- Keeps build-once and deploy-by-digest in the pipeline, because the orchestrator runs whatever reference it is handed
- Knows that rolling back a deployment restores its pod template only, not a config map or secret changed in place
- Knows whether their orchestrator rolls back a stalled rollout by itself — a Kubernetes Deployment reports failure and stops — and says what should decide
- Keeps a deploy record outside the cluster, because revision history is short and records no who or why
- Says two pipeline runs racing to deploy are still the pipeline's problem
Follow-up questions
- Which parts of today's script does the orchestrator make unnecessary?
- Your readiness probe calls `/healthz`. What does `/healthz` check?
- A rollout gets stuck half-way. What happens next, and who decides?
- You roll back the deployment. What is still new?
- Where do you look to find out what was running last Tuesday at 10:00?
Closing
That's the exercise. Ask me whatever you want about how code gets to production here — who can deploy, what the last bad deploy was, what still runs from someone's laptop.
What this line is for
Purpose
Not scored: the close stays off the scorecard. It is the candidate's chance to find out what they would inherit, and anything worth remembering goes in the notes.
Before you decide, one thing you should know: [say one true, specific thing about your own deploy path that this candidate would want to hear before accepting — a script nobody dares to change, a rollback that has never been tried, a runner with more access than anyone on the team]. Whoever takes this job starts there.
What this line is for
Purpose
A real, unflattering detail about your own pipeline is the most persuasive thing you can offer the engineer this round is looking for, and fair warning to one who expected it already fixed. Check it is still true before you say it.
DevOps Engineer interviews — common questions
- Who is this DevOps Engineer interview plan for?
- It is written for the interviewer, not the candidate: the hiring manager, engineer or panel member running the Coding Test round for a DevOps Engineer role. It gives you a 60 min script to follow in the conversation — 4 questions with what each one is for and the signals to score against — so you are not writing the round from scratch the night before.
- What does the Coding Test round assess?
- This round is focused on: A DevOps coding challenge on a deploy script that went green while production was wrong: tracing each incident to its line, rewriting it so success means every host serves this commit's image, recording what ran before so a failure can be undone, and knowing what an orchestrator will not do for you. It works through Two incidents, one script, Green has to mean shipped, A failure someone can recover from and What the orchestrator takes off your hands, scoring against 35 observable signals, with follow-up prompts on all 4 questions for going deeper where an answer is thin.
- How is the 60 min split up?
- 60 min on 4 questions. The questions take in Two incidents, one script (12 min), Green has to mean shipped (20 min), A failure someone can recover from (16 min) and What the orchestrator takes off your hands (12 min). The timings are there so the round stays on schedule and every candidate gets the same shape of interview — which is what makes two candidates comparable afterwards.
- What other rounds should I run for a DevOps Engineer?
A single round does not cover a whole role. The other rounds in this library for a DevOps Engineer:
Hiring for this role?
Open this plan in Hirezen and make it a position in one click.
- Every interviewer runs the same script and marks the same signals.
- AI drafts the write-ups, and the debrief puts every read side by side.
- No ATS to set up first, and no bot in the call.
Free for your first open role.