Docker interview questions and answers
28 Docker questions from real interviews, sorted by seniority: images and layers, networking, volumes, Compose, security and the production failures you get asked to debug out loud.
All 28 questions
1
What is the difference between an image and a container?
JuniorImage Container What it is A read-only template A running instance of one Filesystem Stacked layers, immutable Those layers plus a thin writable layer Lifecycle Built once, reused Created, started, stopped, removed Analogy A class An object One image can back a hundred containers, and each gets its own writable layer, so writes inside one are invisible to the others.
The consequence worth stating out loud: anything written inside a container is lost on
docker rmunless it was written to a volume or a bind mount. That is what interviewers are checking you understand.2
How is a container different from a virtual machine?
JuniorThe guest operating system is the whole difference. A container is a process using the host's kernel, which is why it starts in milliseconds; a VM boots its own kernel, which is why its isolation boundary is stronger and its footprint is gigabytes. Container Virtual machine Virtualises The operating system The hardware Kernel Shares the host's Runs its own guest kernel Start time Milliseconds Tens of seconds Image size Megabytes Gigabytes Isolation boundary The kernel The hypervisor The trade is that boundary. A kernel exploit crosses a container's; it does not cross a VM's. It is also why Linux containers need a Linux kernel, which is why Docker Desktop runs a Linux VM on macOS and Windows.
3
What happens when you run
Juniordocker run nginx?Walk it through in order, because the point of the question is whether you know the steps are separable.
- 1The CLI sends the request to the daemon over
/var/run/docker.sock. - 2The daemon looks for
nginx:latestlocally. If it is missing it resolves the name to Docker Hub and pulls each missing layer. - 3It creates the container: a new writable layer, plus namespaces for process, network, mount, IPC and UTS, and a cgroup for limits.
- 4It attaches the container to the default bridge network and gives it an IP.
- 5It starts the image's entrypoint and command as PID 1 inside the container.
Knowing the daemon does the work, not the CLI, is what makes "Cannot connect to the Docker daemon" a five-second diagnosis later.
- 1The CLI sends the request to the daemon over
4
What is a Docker layer, and why does layer order matter?
JuniorEach instruction in a Dockerfile that changes the filesystem creates a layer. Layers are content-addressed and cached, and a build reuses a cached layer only if that instruction and every instruction before it are unchanged.
Layers are additive and read-only, so a file deleted by a later instruction is still carried in the earlier one. Ordering by rate of change is what makes the cache work: put what changes rarely at the bottom. That is why order matters. Put the parts that change rarely first and the parts that change on every commit last:
COPY package*.json ./ RUN npm ci COPY . .Copying
package.jsonon its own meansnpm ciis only re-run when dependencies actually change. Reverse those and every one-character source edit reinstalls the whole dependency tree.5
What is the difference between COPY and ADD in a Dockerfile?
JuniorCOPYADDCopies from the build context Yes Yes Fetches a URL No Yes Auto-extracts a local tar No Yes Recommended Yes Only when you need those extras Prefer
COPYeverywhere. Both extras are surprising in practice, and a.tar.gzyou meant to copy verbatim getting silently unpacked is a genuinely confusing bug. UseCOPY, and when you need a remote file use an explicitRUN curlso the fetch and its failure mode are visible.6
How do you persist data from a container?
JuniorWith a volume or a bind mount, because the container's own writable layer is deleted with the container.
A named volume is managed by Docker under
/var/lib/docker/volumes. It is the right default for databases and application state: portable across hosts with the right driver, and not tied to a path on this machine.docker run -v pgdata:/var/lib/postgresql/data postgres:16A bind mount maps a specific host path into the container. It is right for development, where you want your source on the host and running in the container.
docker run -v "$PWD":/app node:22A tmpfs mount lives in memory only and never touches disk, which suits secrets and scratch space.
7
What is a multi-stage Docker build, and what problem does it solve?
MidA multi-stage build uses several
FROMinstructions in one Dockerfile and copies only the finished artefacts from the earlier stages into the last one. Only the final stage ships.FROM golang:1.23 AS build WORKDIR /src COPY . . RUN CGO_ENABLED=0 go build -o /app ./cmd/api FROM gcr.io/distroless/static COPY --from=build /app /app ENTRYPOINT ["/app"]It solves two things at once. Size: the compiler, module cache and source never reach the runtime image. Security: a distroless or scratch final image has no shell and no package manager, so there is very little to exploit and very little to appear in a CVE scan.
The point worth making is that deleting build tools in a later
RUNdoes not work. Layers are additive, so the bytes are still in the earlier layer and still in the image.8
What is the difference between CMD and ENTRYPOINT in Docker?
MidENTRYPOINTCMDSets The executable Its default arguments Overridden by docker runargsNo Yes Overridden by --entrypointAny trailing arguments Used together they give a sensible default that stays overridable:
ENTRYPOINT ["ping"] CMD ["-c", "3", "localhost"]docker run imgpings localhost three times.docker run img example.compings example.com, because the CMD was replaced but the entrypoint was not.The second half of the answer is exec form versus shell form.
ENTRYPOINT ["ping"]runs the binary directly as PID 1, so it receivesSIGTERMand can shut down cleanly.ENTRYPOINT pingruns it under/bin/sh -c, the shell becomes PID 1, and it does not forward signals. That is why containers take exactly ten seconds to stop and are then killed: always use exec form.9
Explain Docker's default network drivers and when you would use each.
MidDriver What it does Use it for bridge Private subnet on a virtual bridge, NAT to the outside Almost everything. A user-defined bridge also gives DNS, so containers resolve each other by name host No network isolation, the host's own stack When you genuinely need the host's stack. Linux only, and ports collide none No network at all Jobs that should not have one overlay Spans multiple hosts Swarm and multi-host setups macvlan Own MAC, appears as a device on the LAN Legacy systems that require it The detail worth volunteering: the default
bridgenetwork has no embedded DNS, which is why every tutorial tells you to create your own.In practice: a user-defined bridge for almost everything, host when you genuinely need the host's stack.
10
Two containers are on the same host but cannot reach each other. How do you debug it?
MidEstablish whether they are on the same network first, because the most common cause is that they are not.
docker inspect -f '{{json .NetworkSettings.Networks}}' api dbIf they are both on the default
bridge, name resolution will not work: the default bridge has no embedded DNS. Create a user-defined network and attach both.If they share a user-defined network, check the name being used. Containers resolve each other by container name or network alias, not by image name and not by
localhost.localhostinside a container is that container.Then check the process is listening on the right interface. An application bound to
127.0.0.1inside the container is unreachable from outside it, and has to bind0.0.0.0.docker exec api sh -c 'getent hosts db; nc -zv db 5432'11
What is the difference between a Docker named volume and a bind mount?
MidOwnership and lifecycle.
A named volume is created and managed by Docker. It survives
docker rm, is listed bydocker volume ls, can use a driver to sit on NFS or a cloud disk, and when mounted into an empty directory Docker copies the image's existing contents into it on first use.A bind mount is just a host path. Docker does not manage it, does not know its lifecycle, and never copies image content into it. Mounting a bind mount over a populated directory hides what was there, which is the classic "my
node_modulesdisappeared" problem when the host directory has none.Bind mounts are also a security consideration: mounting
/var/run/docker.sockor/into a container is effectively handing over the host.12
What does
Middepends_onactually guarantee in Docker Compose?Only start order, not readiness.
depends_onwaits for the container to be *started*, not for the process inside it to be *ready*, so an application will happily start and fail to connect to a database that is still initialising.The fix is a condition tied to a healthcheck:
services: db: image: postgres:16 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s retries: 10 api: depends_on: db: condition: service_healthyThe better answer adds that applications should retry their own connections anyway. Dependencies restart in production, and nothing orchestrates start order there.
13
How do you make a Docker image smaller?
MidIn rough order of how much they gain:
- Multi-stage builds. Ship only the artefact, never the toolchain.
- A smaller base.
node:22-alpineagainstnode:22is often 1.1 GB of difference. Distroless or scratch is smaller still where the binary is static. - Fewer, better layers. Chain related commands and clean up in the *same*
RUN, because a later deletion does not shrink an earlier layer:
RUN apt-get update \ && apt-get install -y --no-install-recommends curl \ && rm -rf /var/lib/apt/lists/*- A real `.dockerignore`. Excluding
.git,node_modulesand build output shrinks the context, speeds up the build, and avoids copying secrets in by accident. - Install production dependencies only, with
npm ci --omit=devor the equivalent.
Worth naming the trade: Alpine uses musl rather than glibc, which occasionally breaks native modules and has caused real DNS and performance surprises.
14
A container exits immediately after starting. How do you find out why?
MidStart with the exit code and the logs of the run that failed.
docker ps -a --filter "name=api" --format '{{.Names}}\t{{.Status}}' docker logs apiRead the exit code, because it names the class of fault:
Code Means Usually 0Finished normally There was no long-running process to begin with 1Application error Configuration, or an unreachable dependency 127Command not found A wrong path, or no shell in a slim base image 137SIGKILL The out-of-memory killer 139Segfault A native crash, often an architecture mismatch 143SIGTERM Something asked it to stop If it dies too fast to inspect, override the entrypoint and look around:
docker run --rm -it --entrypoint sh myimageThe most common root cause is a container with nothing to keep it alive. A container runs exactly one foreground process and exits when that process exits.
15
Why should a container not run as root, and how do you avoid it?
MidBecause the root inside the container is, by default, the same UID 0 as root on the host. If an attacker escapes the container, or if a bind mount is writable, that UID is the one doing the damage.
RUN adduser --system --uid 10001 app USER 10001Use a numeric UID rather than a name so Kubernetes can enforce
runAsNonRoot, which cannot verify a username.A strong answer continues to the runtime flags, because the Dockerfile alone is not enough:
docker run --read-only --cap-drop ALL --security-opt no-new-privileges myimageAnd it mentions user namespace remapping, which maps container root to an unprivileged host UID and is the real fix where you can enable it.
16
How do you get a secret into an image without leaking it?
MidNot with
ARG, and not withENV. Both are stored in the image metadata and visible to anyone who runsdocker history, even if a later layer deletes the file.At build time, use BuildKit secret mounts, which are available to one
RUNand never written to a layer:RUN --mount=type=secret,id=npmtoken \ NPM_TOKEN=$(cat /run/secrets/npmtoken) npm cidocker build --secret id=npmtoken,env=NPM_TOKEN .At runtime, inject secrets from outside the image: environment variables from a secret manager, mounted files, or the platform's own mechanism. The rule is that the image is a public artefact and should be safe to push to a registry anyone can read.
17
What is the problem with the Docker
Midlatesttag?latestis not a special pointer to the newest version. It is the default tag applied when you do not give one, and it moves whenever someone pushes to it.That creates three problems. Builds are not reproducible, because the same Dockerfile produces different images on different days. Rollbacks are unclear, because you cannot name the version you want to go back to. And caching gets confusing, since a node may already hold a stale
latestandimagePullPolicy: IfNotPresentwill use it.Tag with something immutable: a semantic version, a git SHA, or a build number. For the strongest guarantee, pin by digest, which is content-addressed and cannot move:
myapp@sha256:9b2b...e4118
How does the Docker build cache decide whether a layer can be reused?
SeniorFor most instructions the daemon hashes the instruction string plus the ID of the parent layer. If both match an existing layer, it is reused. Because the parent is part of the key, a cache miss invalidates every layer after it, which is the whole reason instruction order matters.
COPYandADDare the exception. They also hash the *contents* of the files being copied, so the cache breaks when a copied file changes even though the instruction text is identical. Note this includes file metadata in some cases, which is why achmodin CI can break a cache that looked stable locally.A senior answer adds what this means in CI: a fresh runner has an empty cache, so you need
--cache-fromagainst a registry image, or BuildKit's--mount=type=cachefor package manager directories, or the layers get rebuilt every time regardless of how well the Dockerfile is ordered.19
Your image works locally on an M-series Mac and fails on the cluster with "exec format error". What happened?
SeniorThe image was built for
linux/arm64and the nodes arelinux/amd64. The kernel cannot execute the binary, so it reports an exec format error, which reads like a corrupt file rather than an architecture mismatch.Confirm it in one command:
docker image inspect myapp:1.0 --format '{{.Architecture}}/{{.Os}}'The fix is to publish a multi-architecture manifest rather than building for whatever the developer's laptop happens to be:
docker buildx build --platform linux/amd64,linux/arm64 -t repo/app:1.4.2 --push .The detail that marks experience: in a mixed-architecture cluster the Pod schedules anywhere, so the same image works on some nodes and fails on others. It presents as a flaky deploy, not as a build problem.
20
Which Linux primitives actually provide container isolation?
SeniorThree groups, and it is worth being precise because "containers are lightweight VMs" is the wrong mental model.
Namespaces provide the isolation.
pidgives the container its own process tree so its entrypoint is PID 1.mntgives it its own filesystem view.netgives it its own interfaces and routing table.utsgives it its own hostname.ipcseparates shared memory.usermaps UIDs, so container root can be an unprivileged host user.cgroups provide the limits: CPU shares and quota, memory limits, block IO, and the PID count. A memory limit here is what produces an OOM kill with exit code 137.
Capabilities, seccomp and LSMs reduce what the process may ask the kernel to do. Docker drops most capabilities by default and applies a seccomp profile blocking around forty syscalls.
The conclusion an interviewer wants: it is all one shared kernel, so a kernel vulnerability crosses the boundary, and that is why untrusted multi-tenant workloads use gVisor, Kata or separate VMs.
21
Why does your container take ten seconds to stop, and how do you fix it?
SeniorBecause
SIGTERMis not reaching the application. Docker sendsSIGTERM, waits ten seconds, then sendsSIGKILL. A container that always takes exactly ten seconds is being killed, not stopping.Almost always the cause is shell-form
CMD.CMD npm startruns under/bin/sh -c, so the shell is PID 1 and does not forward signals to its child. Use exec form:CMD ["node", "server.js"]If you genuinely need a shell wrapper,
execthe real process so it replaces the shell and inherits PID 1. Where the application spawns children, add an init such as--initortinito reap zombies and forward signals.The second half is that the application has to handle
SIGTERM: stop accepting new connections, finish in-flight requests, close pools, exit. Without that you drop requests on every deploy.22
How do you handle logging for containers, and why not write to a file?
SeniorContainers should write to stdout and stderr, and let the platform collect them. A log file inside a container disappears with the container, is invisible to
docker logs, and fills the writable layer until the host runs out of disk.Docker's default
json-filedriver is worth knowing because it is unbounded by default. A chatty container will fill/var/lib/docker/containersand take the host down, which surfaces as "no space left on device" for everything else on the box.{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }In a cluster, the node agent tails those files and ships them to a backend. The application should emit structured JSON lines with a correlation ID, since that is what makes them queryable once they arrive.
23
What is a healthcheck, and where does Docker's version not apply?
SeniorHEALTHCHECKin a Dockerfile tells the Docker daemon how to test whether the container is working, and marks it healthy, unhealthy or starting.HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \ CMD curl -fsS http://localhost:8080/healthz || exit 1The important limitation: Kubernetes ignores it entirely. Kubernetes runs its own liveness, readiness and startup probes from the kubelet, and never reads the image's
HEALTHCHECK. Claiming otherwise is a common way to lose credibility in an interview.It is also worth distinguishing the probe types, since conflating them causes real outages: readiness controls whether traffic is sent, liveness controls whether the container is restarted. A liveness probe pointed at a dependency will restart your whole fleet when that dependency has a bad minute.
24
How would you secure a container supply chain end to end?
SeniorCover the four stages rather than listing tools.
Build. Pin base images by digest, not tag. Build from a clean checkout in CI, not from a laptop. Use multi-stage builds so the toolchain never ships, and BuildKit secret mounts so credentials never reach a layer.
Scan. Run an image scanner such as Trivy or Grype in the pipeline and fail on fixable high and critical findings. Generate an SBOM so you can answer "are we affected?" without rebuilding everything. Scan continuously, since a clean image becomes vulnerable when a CVE is published, with no change on your side.
Sign and verify. Sign images with Cosign, and enforce the signature at admission so the cluster refuses anything unsigned or from an unexpected registry.
Run. Non-root with a numeric UID, read-only root filesystem, all capabilities dropped,
no-new-privileges, a seccomp profile, and no/var/run/docker.sockmounted anywhere.The honest closing point is that most real incidents come from the boring end: a leaked registry credential or an over-permissive mount, not a novel kernel escape.
25
Seniordocker system dfshows 40 GB reclaimable. Walk through cleaning it up safely.Look before deleting, and go from safest to least safe.
docker system df -vBuild cache first. It is almost always the largest item and nothing running depends on it:
docker builder pruneThen stopped containers and dangling images. Then images no container is using, which matters on a CI host that has pulled hundreds of tags:
docker image prune -aStop before volumes.
docker volume prunedeletes data, and "unused" only means no container currently references it, which is true of a database volume between deploys. Checkdocker volume lsand know what each one is first.Two things that catch people out: container logs are not counted as reclaimable by
docker system df, so check/var/lib/docker/containersseparately, and a full disk can also be exhausted inodes rather than bytes, whichdf -iwill show anddf -hwill not.26
Where does Docker Compose stop being the right tool?
SeniorCompose describes containers on one host. It has no scheduler, so it cannot place work across machines, cannot reschedule when a host dies, and gives you no rolling update with health gating or automatic rollback. Scaling is manual and bounded by that one machine, and there is no built-in service discovery beyond the single Docker network.
That makes it excellent for local development, integration tests in CI, and genuinely small single-host deployments where an hour of downtime is acceptable.
Past that you want an orchestrator for scheduling, self-healing, rolling deploys, secret management and horizontal scaling. A good answer resists saying "always Kubernetes": Kubernetes has real operational cost, and ECS, Nomad or a managed platform are often the better trade for a small team.
27
How do you debug inside a container that has no shell?
MidDistroless and scratch images have no shell on purpose, so
docker exec -it shfails. There are three options.Attach a debug container that shares the target's namespaces, which gives you your tools against their process tree and network:
docker run -it --rm --pid=container:api --network=container:api nicolaka/netshootIn Kubernetes the same idea is a built-in:
kubectl debug -it api-7d4f --image=nicolaka/netshoot --target=apiOr inspect from outside:
docker logs,docker inspect,docker diffto see what the container has written, anddocker cpto pull a file out.Worth saying that you would not add a shell to the production image to make debugging easier. That removes the reason the image is hardened.
28
What does
Junior.dockerignoredo, and why does it matter?It excludes paths from the build context, which is the set of files the CLI sends to the daemon before the build starts.
It matters for three reasons. Speed: sending a 500 MB
.gitdirectory and localnode_moduleson every build is slow. Caching: aCOPY . .invalidates its layer whenever any copied file changes, including files the build does not need. Security: without it, a.envfile or a private key in the working directory can be copied straight into an image you then push to a registry.A reasonable starting point:
.git node_modules npm-debug.log .env .env.* dist coverage Dockerfile .dockerignore
Live exercise: a container will not stay up
This is the most commonly asked Docker debugging question, and it is asked as a conversation. Narrate this order and you will cover every cause without guessing.
Is it exited, or is it restarting?
docker ps -a --format '{{.Names}}\t{{.Status}}'- yes
- Exited once means it ran and stopped, so read the exit code next.
- no
- Restarting repeatedly means it is crash-looping, so you need the logs of the run that failed, not the one starting now.
Does the log show an application error?
docker logs --tail 50 myapp- yes
- The application itself failed. That is configuration, a missing environment variable, or an unreachable dependency at startup.
- no
- An empty log usually means the process was killed rather than that it failed, so stop reading logs and read the exit code.
Is the exit code 137?
docker inspect myapp --format '{{.State.ExitCode}} {{.State.OOMKilled}}'- yes
- The kernel killed it for exceeding the memory limit. Compare real usage against the limit, and check whether the runtime is sizing its heap from the host rather than the cgroup.
- no
- Exit 0 means the process completed, so there was no long-running process to begin with. 127 or 128 means the entrypoint path does not exist. 139 is a segfault.
Does it die too fast to inspect?
docker run --rm -it --entrypoint sh myimage- yes
- Override the entrypoint and look around: check the binary exists, is executable, and is built for this architecture.
- no
- Exec in and check the thing the logs pointed at, usually environment variables or connectivity to a dependency.
What each level is testing
- 1
Junior7 questions
Whether the model is right. Image against container, why data disappears, what a layer is, and what
docker runactually does. Definitions are fine here, as long as you can say the consequence: not just "images are read-only" but "so anything written in the container is gone unless it went to a volume". - 2
Mid12 questions
Whether you have shipped something. Multi-stage builds, why
CMDandENTRYPOINTdiffer, how containers find each other, and how you debug one that will not start. Expect to be asked what you would change about a Dockerfile you are shown. - 3
Senior9 questions
Judgement and trade-offs. How the build cache actually keys, signal handling and graceful shutdown, what the kernel primitives really provide, and where you would not use containers. The questions become open ones where naming the trade matters more than the answer.
What the round is like
A Docker round is usually thirty to forty minutes and moves in three stages. It opens with definitions to check you are not bluffing, moves to how you would build and ship an image, and ends with a failure you have to debug out loud. The last stage is where offers are decided, because anyone can recite what a layer is and far fewer can say why a container takes exactly ten seconds to stop.
How to prepare with this
- 1Filter to your level first and attempt each question out loud before opening the answer. Reading an answer you did not attempt feels like learning and is not.
- 2Mark a question known only when you can say it without the page. The button is there so the second pass is short.
- 3Do the senior questions even for a mid-level role. Interviewers commonly push one level past the job to find your edge, and the graceful-shutdown and build-cache answers are where most candidates stop.
- 4For anything you get wrong, go and break it. Run a container with a shell-form
CMDand timedocker stop. The ten seconds is the part you will remember. - 5Prepare one real story about a container problem you fixed. Almost every round ends with some form of "tell me about a time", and a specific failure beats a general answer.