Docker vs Terraform Interview Questions: Containers vs Infrastructure as Code
Reviewed by Mark Dickie · Last updated
Docker and Terraform are both core tools in modern DevOps workflows, but they solve different problems: Docker packages and runs applications inside containers, while Terraform declaratively provisions and manages infrastructure across cloud providers. For an interview, the two rarely compete directly — a question about Docker image layering is a completely different muscle from one about Terraform state management — yet engineers increasingly need a working grasp of both, and interviewers know it. Candidates who treat them as interchangeable "DevOps things" tend to stumble on precisely the questions that probe the boundary: when do you reach for a Dockerfile versus a Terraform resource block, and what breaks if you confuse the two?
How they differ as interview subjects
| Docker | Terraform | |
|---|---|---|
| Core concept tested | Container lifecycle, image builds, networking, volumes | Infrastructure state, provider config, plan/apply cycle |
| Typical question style | Debugging scenarios, Dockerfile optimisation, runtime flags | Declarative syntax, state file handling, module design |
| Where candidates drop marks | Layer caching rules, multi-stage builds, inter-container networking | State drift, remote backends, destroy vs taint semantics |
| Closest real-world role | Application packaging, local dev, CI pipelines | Cloud provisioning, platform engineering, IaC at scale |
| Difficulty curve | Entry questions are accessible; depth comes from internals | Conceptual floor is higher; state management trips many mid-levels |
How to decide which to focus on
- Check the job description first. A backend engineer role will weight Docker heavily; a platform or SRE role almost always expects Terraform fluency. Study the one in the title, then shore up the other.
- Know your own gaps honestly. If you have run
docker rundaily but never written aterraform plan, your Terraform answers will read as surface-level. Interviewers hear it quickly. - Prepare the overlap zone. Questions about running Terraform inside a Docker container, or about managing container registries with Terraform, appear at mid-to-senior level. Expect at least one.
- Match depth to seniority. Junior interviews tend to stay on Docker basics and Terraform syntax. Senior rounds go into multi-stage build strategy, remote state locking, and workspace design — be ready to discuss trade-offs, not just commands.
- Use the decision table below. The attempt data on this page shows which specific questions candidates miss most often in each technology. That miss-rate pattern is a more direct study guide than any syllabus.
Docker vs Terraform, side by side
How Docker and Terraform compare on Tarmac’s interview questions.
| Metric | Docker | Terraform |
|---|---|---|
| Practice questions | 6 | 6 |
| Average score | — | — |
| Hardest question (% who miss it) | — | — |
| Average time per question | — | — |
Practice questions
You need to copy a local requirements.txt from the build context into the image. Docker's own best-practice guidance says to prefer one instruction here. Which, and why?#
Options
COPY, because it only copies local files and does nothing implicit —ADDalso fetches URLs and auto-extracts archives, which is surprisingADD, becauseCOPYcannot copy a single file, only whole directoriesADD, becauseCOPYdoes not preserve file permissions- Either is identical;
COPYis just a newer alias forADD
Show answer
Prefer COPY for copying local files into an image. COPY only copies files and directories from the build context, so its behaviour is transparent and predictable. ADD does extra implicit work — it can fetch remote URLs and auto-extracts local tar archives — which makes builds surprising. Both preserve permissions and handle single files, so reserve ADD for the narrow case where you actually want local-archive extraction.
Docker's Dockerfile best practices recommend COPY for plain file copies because its behaviour is transparent: it only copies local files/directories from the build context. ADD does extra implicit work — it can download remote URLs and will auto-extract local tar archives — which makes builds less predictable and can mask surprises. Both preserve permissions and both handle single files. Reaching for ADD 'just in case' is the misconception; use ADD only when you specifically want local-archive auto-extraction.
What is the primary purpose of the Terraform state file (terraform.tfstate)?#
Options
- It maps the resources in your configuration to the real-world infrastructure objects Terraform manages
- It stores your provider credentials so you don't re-enter them each run
- It is a human-authored file where you declare the desired infrastructure
- It caches downloaded provider plugins to speed up
terraform init
Show answer
The state file maps each resource address in your configuration to the real infrastructure object Terraform created (an instance id, a bucket ARN). Terraform reads it to compute the diff between desired and actual state, so it knows what to create, update, or destroy. It is not where you declare desired infrastructure (that's your .tf files), and it is not a credential or plugin cache.
State is Terraform's record of which real object (an AWS instance id, a bucket ARN) corresponds to each resource address in your config. Without it Terraform couldn't tell what already exists, so it couldn't compute a diff or know what to update or destroy. Credentials are supplied by the provider config/environment, the desired state is your .tf files, and plugins are cached under .terraform/. Losing or corrupting state is what makes Terraform try to recreate or orphan live infrastructure.
Your Dockerfile has EXPOSE 8080. You run the container with docker run myimg (no -p). Can a client on the host reach the app on port 8080?#
Options
- No —
EXPOSEis only metadata; you must publish the port with-p 8080:8080for host traffic to reach the container - Yes —
EXPOSEautomatically publishes the port to the host - Yes, but only from other containers on the same network, never from the host
- No —
EXPOSEblocks the port unless you also add--publish-all
Show answer
No. EXPOSE is only metadata — it records which port the application listens on but never publishes it. For host traffic to reach the container you must publish the port explicitly with -p 8080:8080, or auto-publish all exposed ports with -P. Publishing with -p even works without an EXPOSE line. Assuming EXPOSE opens the port is a common cause of an unreachable container.
EXPOSE is documentation/metadata: it records which port the app listens on but never publishes anything. To let host traffic reach the container you publish the port with -p 8080:8080 (or auto-publish all exposed ports with -P). You don't even need EXPOSE to publish — -p works regardless. The trap is assuming EXPOSE opens the port; teams hit this when a 'working' image is unreachable in production because nobody published the port.
Which of the following are valid arguments inside a resource's lifecycle {} block? Select all that apply.#
Options
create_before_destroyprevent_destroyignore_changesdepends_on
Show answer
The lifecycle block accepts create_before_destroy (reverse the replace order to avoid downtime), prevent_destroy (reject plans that would destroy the resource), and ignore_changes (ignore drift on listed attributes), along with replace_triggered_by. depends_on is also a meta-argument but belongs at the resource level, not inside lifecycle. Placing it inside lifecycle is a frequent mistake.
create_before_destroy, prevent_destroy, and ignore_changes (plus replace_triggered_by) are the lifecycle meta-arguments: they tune replacement order, block destroys, and tell Terraform to ignore drift on chosen attributes. depends_on is a meta-argument too, but it sits at the resource level, not inside lifecycle — putting it there is a common authoring error. Knowing what lifecycle controls is what lets you do a zero-downtime replacement or stop an external autoscaler's changes from showing up as perpetual drift.
Why does Docker recommend the exec form ENTRYPOINT ["node", "server.js"] over the shell form ENTRYPOINT node server.js for a long-running service?#
Options
- Exec form runs the process directly as PID 1, so it receives signals like SIGTERM and can shut down gracefully; shell form wraps it in
/bin/sh -c, which becomes PID 1 and often doesn't forward signals - Exec form is faster because it skips parsing the Dockerfile
- Shell form cannot pass any arguments to the process
- Exec form automatically restarts the process if it crashes
Show answer
Exec form launches your process directly so it becomes PID 1 and receives signals — including the SIGTERM that docker stop sends — letting it shut down gracefully. Shell form runs the command through /bin/sh -c, so the shell becomes PID 1 and often fails to forward SIGTERM to your process; docker stop then waits the grace period and SIGKILLs it. That is the usual cause of containers that take ten seconds to stop.
With exec form, your process is launched directly and becomes PID 1, so it receives signals (notably the SIGTERM docker stop sends) and can drain connections and exit cleanly. Shell form runs /bin/sh -c "node server.js", making the shell PID 1; the shell frequently doesn't forward SIGTERM to its child, so docker stop hangs for 10s and then SIGKILLs — abrupt termination, dropped requests. Shell form does pass args and isn't slower; it just breaks signal handling. This is the classic 'my container takes 10 seconds to stop' bug.
Marking an output with sensitive = true encrypts that value so it is no longer stored in plaintext in the state file.#
Options
- True
- False
Show answer
False. sensitive = true only hides a value from CLI output, showing <sensitive> in plan and apply logs; the value is still stored in plaintext in the state file, and terraform output -json or -raw reveals it. Keeping a value out of state requires an ephemeral output. Real protection comes from encrypting the backend and restricting access to state, not from the sensitive flag.
False. sensitive = true only redacts the value from CLI plan/apply output (it shows as <sensitive>); the value is still written to state in plaintext, and terraform output -json/-raw will print it. Anyone who can read the state file can read the secret. To keep a value out of state entirely you use an ephemeral output (Terraform 1.10+). Believing sensitive encrypts state is a dangerous assumption — protect state with backend encryption and access controls instead.
What is the difference between docker run and docker exec?#
Options
docker runcreates and starts a new container from an image;docker execruns a command inside an already-running containerdocker runruns in the foreground;docker execruns the same container in the backgrounddocker runis for images anddocker execis for images too, butexecskips the entrypoint- They are aliases;
execis the older name kept for compatibility
Show answer
docker run <image> creates a new container from an image and starts its main process. docker exec <container> <command> runs an extra command inside a container that is already running — so the target must already exist and be up; you cannot exec into a stopped container. exec is the tool for debugging a live container (docker exec -it web sh) without disturbing its main process.
docker run <image> creates a brand-new container from an image and starts its main process. docker exec <container> <cmd> runs an additional command inside a container that is already running — the target must exist and be running, so you can't exec into a stopped or nonexistent container. You use exec for debugging (docker exec -it web sh) without disturbing the main process. Confusing the two leads people to spin up a fresh container when they meant to inspect a live one.
Given this locals block, what is the resulting value of local.tags (evaluated with Terraform's merge function)?#
locals {
defaults = { env = "dev", team = "core" }
overrides = { env = "prod" }
tags = merge(local.defaults, local.overrides)
}Options
{ env = "prod", team = "core" }{ env = "dev", team = "core" }{ env = "dev", env = "prod", team = "core" }- An error: duplicate key
envacross the merged maps
Show answer
`{ env = "prod", team = "core" }`
merge combines maps left to right, and when the same key appears more than once the value from the later argument wins. So env from overrides (prod) overrides env from defaults (dev), while team is carried through unchanged. Duplicate keys across arguments are not an error — last-wins is the whole point. This precedence is the standard pattern for a base tag/config map overridden per environment; getting the argument order backwards silently applies the wrong values.
A container writes a file to its own filesystem (not a volume). You then docker rm the container and start a fresh one from the same image. Is the file there?#
Options
- No — writes go to the container's thin writable layer, which is discarded when the container is removed; the image's read-only layers are unchanged
- Yes — writes are committed back into the image automatically
- Yes — the writable layer is shared across all containers from that image
- Only if the file is smaller than the image's layer size limit
Show answer
No. A container's writes go to a thin writable layer added on top of the image's read-only layers via copy-on-write. Removing the container discards that writable layer, so the file is lost, and the image itself was never modified — a fresh container starts clean. Writes are not committed back into the image, and each container has its own writable layer. Durable data must live in a volume or bind mount.
An image is a stack of read-only layers. Starting a container adds a thin writable layer on top (copy-on-write): all changes the container makes live only there. Removing the container deletes that writable layer, so the file is gone, and the image's layers were never modified — a new container starts clean. Writes are never committed back to the image automatically, and each container gets its own writable layer (they aren't shared). This is exactly why durable data must go in a volume or bind mount, not the container filesystem.
This configuration fails with a Self-referential block / cycle error during terraform plan. Which line is the cause?#
1| resource "aws_security_group" "web" {
2| name = "web-sg"
3| ingress {
4| from_port = 443
5| to_port = 443
6| protocol = "tcp"
7| security_groups = [aws_security_group.web.id]
8| }
9| }Options
- Line 7 — the resource references its own
.idin its arguments, which Terraform cannot resolve because the id isn't known until the resource is created - Line 2 —
nameis a reserved attribute and cannot be set on a security group - Line 3 — an
ingressblock must be a separateaws_security_group_ruleresource - Line 6 —
protocolmust be a number, not the string"tcp"
Show answer
Line 7 — the resource references its own .id in its arguments, which Terraform cannot resolve because the id isn't known until the resource is created
Line 7 makes the resource depend on its own computed id, which doesn't exist until the resource is created — a dependency cycle Terraform's graph rejects. To allow a security group to reference itself you set self = true on the rule instead of listing its own id. name is settable, inline ingress blocks are valid (a separate aws_security_group_rule is just an alternative), and protocol = "tcp" is correct. Self-reference cycles are a classic Terraform graph error that the message can make cryptic.
You need a private registry token available only while installing dependencies during the build, and it must NOT end up readable in the final image. Select every approach that leaks the token into the image's layers or history.#
Options
- Pass it via
ARG TOKENand reference$TOKENin aRUN— its value is visible indocker history - Set it with
ENV TOKEN=...in the Dockerfile so the install step can read it - Write it to a file in one layer and
rmthe file in a later layer - Mount it with
RUN --mount=type=secret,id=token(BuildKit), reading it only during that step
Show answer
ARG and ENV values are baked into image metadata and recoverable with docker history --no-trunc, and ENV also persists into every container's environment. Writing a secret then rm-ing it later does not help either: layers are immutable and retained, so the file survives in the earlier layer. The only safe approach is a BuildKit secret mount (RUN --mount=type=secret), which exposes the value to one RUN step and never writes it to any layer.
ARG and ENV values are baked into image metadata and are recoverable with docker history --no-trunc (and ENV also persists into every running container's environment). Writing the secret then rm-ing it in a later layer doesn't help: each layer is immutable and retained, so the earlier layer still contains the file even though the final filesystem hides it — anyone can extract it from the layer. The only safe option is a BuildKit secret mount (RUN --mount=type=secret), which exposes the secret to a single RUN and never writes it to any layer. Believing a later rm 'deletes' a secret is the most dangerous misconception here.
Your team currently keeps terraform.tfstate on each engineer's laptop. Explain why you'd move to a remote backend (e.g. S3, Terraform Cloud) and what two problems it solves for a team.#
Show answer
A local state file means each engineer has their own copy, so the state diverges and is never authoritative; it also isn't backed up and can be lost or committed to git with secrets in it. A remote backend stores a single shared state in a central, durable location so the whole team plans and applies against the same source of truth. Crucially it enables state locking: while one apply holds the lock, others are blocked, preventing two people from writing state at once and corrupting it. It also gives durability/versioning (S3 versioning, backups) and keeps the (plaintext) state off laptops behind access controls and encryption at rest.
The two big wins are a single shared source of truth (no divergent laptop copies) and state locking to prevent concurrent applies from corrupting state. Strong answers also mention durability/versioning and that state holds secrets in plaintext, so a remote backend with encryption and access control is a security improvement too. This is the question that separates someone who has run Terraform solo from someone who has run it on a team.