Images & Layers • Dockerfiles • Volumes & Networking • Compose • Security • 2026

Docker Interview Questions

30 questions What each one tests, an answer frame, a spoken answer 35 min read

This page is for developers, DevOps and platform engineers facing a Docker round, from a first job to a senior role. Most rounds open with containers versus virtual machines and images versus containers, then test how layers and caching work, what a good Dockerfile looks like, and the CMD versus ENTRYPOINT trap. After that come volumes, networking, Compose and secrets, and finally debugging a container that keeps dying and keeping images secure. Each question shows what the interviewer is really checking, the shape of a strong answer and a short answer you can say out loud. Swap the stories for your own.

Search all questions by round, difficulty and level, or save the ones you want to practise.

Core Concepts 3 questions

Easy Technical round Fresher, Mid-level Practice question

1. How is a container different from a virtual machine, and when would you still pick a VM?

What the interviewer is really testing:
Whether you know that containers share the host kernel, and what that means for speed, density and isolation, instead of calling a container a lightweight VM and stopping there.
Answer frame:

VM: a hypervisor runs a full guest operating system with its own kernel for each machine.

Container: an isolated process on the host, sharing the host kernel, fenced off with namespaces and limited with cgroups.

Trade-off: containers start in seconds and pack densely; VMs give a stronger isolation boundary and can run a different kernel.

Sample spoken answer:

"A virtual machine emulates hardware, so each one boots its own full operating system with its own kernel on top of a hypervisor. A container doesn't do that. It's really just a process on the host that the kernel isolates: namespaces give it its own view of processes, network and files, and cgroups cap how much CPU and memory it can use. Because there's no guest kernel to boot, a container starts in about a second and I can run many more of them on the same box. The catch is that every container shares the host kernel, so the isolation is thinner. I'd still pick a VM when I need a different kernel or operating system, or a hard security boundary between untrusted tenants. In practice the two stack: most containers in the cloud run on VMs."

Red flag to avoid:

Saying each container has its own operating system or kernel, or that containers are simply smaller VMs.

They may ask next:
  • Docker on a Mac runs Linux containers. How is that possible if containers share the host kernel?
  • Why can a kernel bug be more serious for containers than for VMs?
Say it in 60 seconds
Easy Technical round Fresher Practice question

2. What is the difference between a Docker image and a container?

What the interviewer is really testing:
Whether you have the basic model right: a read-only template versus a running instance with its own writable layer and state.
Answer frame:

Image: a read-only stack of filesystem layers plus config such as the default command, environment and ports.

Container: a created or running instance of an image, with a thin writable layer on top.

Lifecycle: one image can back many containers; a stopped container still exists until you remove it.

Sample spoken answer:

"An image is the template. It's a read-only stack of filesystem layers, plus some config like the default command, environment variables and the working directory. A container is what I get when I run that image: Docker adds a thin writable layer on top, sets up the isolation, and starts the process. I can start ten containers from the same image and they all share the same read-only layers, each with its own writable layer, so they don't see each other's changes. It's a bit like a class and its objects. One thing people miss is that a stopped container isn't gone. It still shows up in docker ps -a with its writable layer intact until I run docker rm, and only then is anything written inside it lost."

Red flag to avoid:

Using the two words as if they mean the same thing, or thinking that editing files in a container updates the image.

They may ask next:
  • If you change a file inside a running container, does the image change?
  • How would you turn a container's changes into a new image, and why is that usually a bad idea?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

3. Under the hood, what Linux features make a container work? Walk me through namespaces and cgroups.

What the interviewer is really testing:
Whether you see a container as an ordinary process with kernel features applied, which is what lets you reason about security, limits and odd behaviour.
Answer frame:

Namespaces: control what a process can see: its own PID tree, network stack, mounts, hostname, IPC and user IDs.

Cgroups: control how much it can use: memory, CPU, number of processes, disk I/O.

The rest: a layered filesystem for the root, plus dropped capabilities and a seccomp filter to trim what it may do.

Sample spoken answer:

"A container is a normal Linux process with a few kernel features wrapped around it. Namespaces decide what it can see. In its own PID namespace it thinks it's process 1, in its own network namespace it gets its own interfaces and ports, the mount namespace gives it its own root filesystem, and there are namespaces for hostname, IPC and user IDs too. Cgroups decide how much it can use: a memory ceiling, a CPU share or quota, a cap on the number of processes. On top of that, the root filesystem is built from the image layers with an overlay filesystem, and Docker drops most Linux capabilities and applies a seccomp profile so the process can't make certain system calls. You can prove it's just a process: run a container, then run ps on the host and you'll see it there with a normal host PID."

Red flag to avoid:

Describing Docker as a hypervisor, or not being able to say what limits memory versus what hides other processes.

They may ask next:
  • What does running a container with --privileged turn off?
  • What is a user namespace, and how does rootless mode use it?
Say it in 60 seconds

Images & Builds 6 questions

Medium Technical round Fresher, Mid-level Practice question

4. How does Docker's build cache work, and why does the order of instructions in a Dockerfile matter?

What the interviewer is really testing:
Whether you can make builds fast on purpose by understanding when a cached layer is reused and when everything after it rebuilds.
Answer frame:

Layers: RUN, COPY and ADD each add a filesystem layer; the build reuses a layer when its inputs are unchanged.

Cache key: for RUN it's the command text on the same parent; for COPY and ADD it's also the contents of the files copied.

Chain: once one step misses the cache, every step after it rebuilds, so put rarely changing steps first.

Sample spoken answer:

"Each RUN, COPY or ADD produces a layer, and when I rebuild, Docker checks whether it already has that layer for the same parent. For a RUN step it compares the command text, and for COPY it also checksums the files being copied. If nothing changed, it reuses the layer instantly. The key rule is that once one step misses the cache, every step after it is rebuilt. So order matters a lot. If I copy my whole source tree before installing dependencies, any one-line code change invalidates the install step and I wait for every package again. If I copy just the dependency manifest, install, and only then copy the source, a code change reuses the install layer. One gotcha: because RUN is keyed on the text, a step like apt-get update can stay cached for weeks, so I keep update and install in the same RUN."

Red flag to avoid:

Believing Docker notices when a remote package changed for a cached RUN step, or copying all source code before installing dependencies.

They may ask next:
  • How do you force a clean build without the cache?
  • Your CI runners start fresh every time. How do you get cache hits there?
Say it in 60 seconds
Medium Coding round Fresher, Mid-level Practice question

5. Write a Dockerfile for a small Node.js API that rebuilds quickly when only the code changes and doesn't run as root.

What the interviewer is really testing:
Whether you can apply caching, a sensible base image and a non-root user in real Dockerfile lines rather than just describing them.
Answer frame:

Base: a slim official image with a fixed version tag rather than latest.

Cache: copy the package manifest and lock file, install, then copy the rest of the source.

Runtime: production dependencies only, a non-root user and an exec-form CMD.

Sample spoken answer:

"I start from a slim Node image with a fixed version tag so I'm not pulling a whole Debian toolchain, and I set a working directory. Then I copy only package.json and the lock file and run npm ci with dev dependencies left out. npm ci installs exactly what the lock file says, so builds are repeatable. Only after that do I copy the rest of the source, which means a normal code change reuses the install layer and the build takes seconds. I switch to the node user that the official image already ships with, so the process isn't root. EXPOSE is just documentation of the port. The CMD is in exec form, so node is the main process and gets the stop signal directly, and the app handles SIGTERM so it shuts down cleanly. I'd pair this with a .dockerignore that leaves out node_modules, .git and any .env files."

Code:
FROM node:22-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
USER node
EXPOSE 3000
CMD ["node", "server.js"]
Red flag to avoid:

Copying the whole project before installing dependencies, or leaving the container running as root without a reason.

They may ask next:
  • The app needs a build step with dev dependencies, like TypeScript. How does this file change?
  • Why npm ci instead of npm install here?
Say it in 60 seconds
Medium Technical round Fresher, Mid-level Practice question

6. What is a multi-stage build, and what problem does it solve? Show a quick example.

What the interviewer is really testing:
Whether you know how to keep compilers, build tools and source code out of the image you ship.
Answer frame:

Stages: several FROM lines in one file; each stage starts from a clean base and can be named.

Copy across: COPY --from pulls only the built output from an earlier stage.

Payoff: a much smaller final image with fewer packages to patch and no build tools for an attacker.

Sample spoken answer:

"A multi-stage build puts more than one FROM in the same Dockerfile. The first stage is a full build environment: compiler, package manager, source code, everything. The last stage starts again from a tiny runtime base and uses COPY --from to take only the finished artifact out of the build stage. Nothing else from the build stage ends up in the final image. For a Go service, the build stage weighs several hundred megabytes, while the final image is basically one static binary on a distroless base, often just a few tens of megabytes. That means faster pulls and deploys, and far fewer packages to show up in a vulnerability scan. It also replaces the old pattern of keeping two Dockerfiles and a shell script to glue them together."

Code:
FROM golang:1.24 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /out/app ./cmd/app

FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]
Red flag to avoid:

Thinking a multi-stage image carries every stage's layers, or deleting build tools with RUN rm and expecting the image to shrink.

They may ask next:
  • How would you run the tests inside the build without shipping them?
  • Can you build just one stage of the file on its own?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

7. How do you choose a base image? Compare a full image, slim, Alpine and distroless.

What the interviewer is really testing:
Whether you weigh size and attack surface against compatibility and debuggability, instead of always reaching for the smallest image.
Answer frame:

Full and slim: Debian or Ubuntu based, glibc, easy to debug; slim drops docs and extra packages.

Alpine: very small and uses musl libc, which can break prebuilt binaries or force slow native builds.

Distroless and scratch: only the runtime, no shell or package manager; smallest attack surface, hardest to debug.

Sample spoken answer:

"I think of it as a trade between size, security and how painful it is to debug. A full image based on Debian has everything, which is handy while building but brings hundreds of packages I'll have to patch. Slim is the same family with the extras stripped out, and it's my default for most runtime images. Alpine is tiny, but it uses musl instead of glibc, so some prebuilt binaries don't work, and for Python it can mean compiling packages from source, which makes builds slower and sometimes bigger. Distroless has only the language runtime and its libraries, no shell and no package manager, which is great for production but means I can't just exec in and poke around. Scratch is empty, which works for static Go binaries. Whatever I pick, I pin the version and rebuild regularly to pick up security fixes."

Red flag to avoid:

Saying Alpine is always the best choice because it's smallest, with no mention of musl or debugging trade-offs.

They may ask next:
  • How do you debug a container built on distroless when there's no shell?
  • What does pinning by digest give you that pinning by tag does not?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

8. You delete a large file in a later RUN step, but the image doesn't get any smaller. Why, and how do you fix it?

What the interviewer is really testing:
Whether you truly understand that layers only add, which matters for both image size and leaked secrets.
Answer frame:

Layers only add: each layer records changes; a delete is stored as a marker that hides the file.

Still there: the bytes remain in the earlier layer and are pulled with the image.

Fix: create, use and delete in one RUN, use a multi-stage build, or use a build mount so the file never enters a layer.

Sample spoken answer:

"Image layers are stacked and each one only records changes on top of the one below. When a later step deletes a file, the new layer just adds a whiteout marker that hides it from the final view. The original bytes are still in the earlier layer, and everyone who pulls the image downloads them. So downloading a big archive in one RUN and removing it in the next saves nothing. The fix is to do the download, the use and the cleanup in a single RUN, so the file never lands in a committed layer. For build tools, a multi-stage build is cleaner, because the final stage never had them. This matters even more for secrets: a key copied in and deleted later can be pulled out of the old layer by anyone with the image. I check with docker history, which shows each layer's size."

Red flag to avoid:

Saying a later rm frees the space, or believing a deleted secret is safe because it isn't visible inside the running container.

They may ask next:
  • How would you spot which layer is making an image big?
  • A private key was baked into an image that's already in the registry. What do you do?
Say it in 60 seconds
Easy Technical round Fresher, Mid-level Practice question

9. What is the build context, and why should every project have a .dockerignore file?

What the interviewer is really testing:
Whether you know what the builder can see during a build, and the speed, cache and secret-leak problems a missing .dockerignore causes.
Answer frame:

Context: the folder you pass to docker build; COPY and ADD can only read files from it.

Ignore file: patterns that exclude files from the context, much like a .gitignore.

Why: faster builds, fewer cache misses and no .env files, keys or .git history copied into images.

Sample spoken answer:

"When I run docker build with a dot at the end, that dot is the build context: the folder the builder is allowed to read from. COPY and ADD can only pull files from inside it, which is why you can't copy something from a parent directory. A .dockerignore lists what to leave out, and I always add one. Without it, a COPY . . drags in node_modules, the .git folder, local build output and, worst of all, .env files or keys that were never meant to leave my laptop. Those end up baked into the image. It also hurts caching, because changes to files that don't matter, like a log file or the git index, change the checksum of the copy step and force a rebuild. So it's faster, the cache works better, and it keeps secrets out."

Red flag to avoid:

Not knowing COPY can't reach outside the context, or treating .dockerignore as a nice-to-have with no security angle.

They may ask next:
  • Your build says a file isn't found even though it's right there in the repo. What would you check?
  • Can one repository have different ignore rules for different Dockerfiles?
Say it in 60 seconds

Dockerfile Instructions 3 questions

Medium Technical round Fresher, Mid-level Practice question

10. What is the difference between CMD and ENTRYPOINT, and how do they behave when used together?

What the interviewer is really testing:
Whether you can predict what actually runs when someone passes arguments to docker run, which trips up many people.
Answer frame:

CMD: the default command or default arguments; replaced by anything after the image name in docker run.

ENTRYPOINT: the fixed executable; only replaced with the --entrypoint flag.

Together: in exec form, CMD's values are passed as arguments to ENTRYPOINT.

Sample spoken answer:

"CMD sets the default. If I type anything after the image name in docker run, that replaces CMD entirely. ENTRYPOINT sets the executable that always runs, and the only way to change it is the --entrypoint flag. Used together in exec form, the JSON array style, CMD becomes default arguments to ENTRYPOINT. So if ENTRYPOINT is the app binary and CMD is --help, running the image bare prints help, and running it with serve passes serve instead. That makes the image feel like a command-line tool. A common pattern is an entrypoint shell script that does setup, like waiting for a config file, and then execs whatever CMD says. One trap: if ENTRYPOINT is written in shell form, as a plain string, CMD and any run arguments are ignored completely."

Code:
ENTRYPOINT ["/usr/local/bin/report-tool"]
CMD ["--help"]

# docker run report-tool            -> report-tool --help
# docker run report-tool export csv -> report-tool export csv
Red flag to avoid:

Saying they are interchangeable, or not knowing that arguments after the image name replace CMD.

They may ask next:
  • What's the difference between the exec form and the shell form of these instructions?
  • How would you get a shell in an image whose ENTRYPOINT is a binary?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

11. Your container takes about ten seconds to stop every time and never shuts down cleanly. What's going on?

What the interviewer is really testing:
Whether you understand how docker stop sends signals, why PID 1 is special, and how shell-form commands swallow SIGTERM.
Answer frame:

docker stop: sends SIGTERM, waits a grace period of ten seconds by default, then sends SIGKILL.

Cause: the shell is PID 1 and doesn't pass the signal on, or the app is PID 1 and never handles SIGTERM.

Fix: exec form, exec in entrypoint scripts, handle SIGTERM in the app, or add a tiny init with --init.

Sample spoken answer:

"A ten-second stop almost always means the signal isn't reaching the app. docker stop sends SIGTERM to process 1 in the container, waits ten seconds by default, and then kills it with SIGKILL. If the CMD is in shell form, process 1 is a shell running my command as a child, and the shell often doesn't pass SIGTERM along, so the app never hears it and gets killed hard at the deadline. Open connections are cut and nothing is flushed. There's a second catch: the kernel treats process 1 specially and won't apply the default action for a signal it hasn't set a handler for, so even an app running as PID 1 has to handle SIGTERM itself. The fixes are exec form, exec in any wrapper script so the app replaces the shell, and a SIGTERM handler in the code. Or I add --init, which puts a tiny init in front that forwards signals and reaps child processes."

Code:
#!/bin/sh
set -e
# setup work here, then hand over PID 1 to the real process
exec "$@"
Red flag to avoid:

Blaming Docker for being slow, or not knowing that shell-form commands put a shell in front of the app.

They may ask next:
  • What is a zombie process, and why can PID 1 in a container end up collecting them?
  • How would you give a slow-draining service longer than ten seconds to shut down?
Say it in 60 seconds
Easy Technical round Fresher Practice question

12. COPY and ADD both put files into an image. What does ADD do extra, and which do you use by default?

What the interviewer is really testing:
Whether you know ADD's hidden behaviours and prefer the instruction whose effect is obvious to the next reader.
Answer frame:

COPY: copies files and folders from the build context, nothing more.

ADD extra: unpacks a local tar archive automatically and can fetch a file from a URL.

Default: COPY, and ADD only when you want the auto-extract on purpose.

Sample spoken answer:

"COPY does exactly one thing: it copies files or folders from the build context into the image. ADD does that too, but it has two extra behaviours. If the source is a local tar archive, it unpacks it into the destination automatically. And it can take a URL and download the file into the image. Those extras are why I use COPY by default. With ADD, someone reading the Dockerfile has to know whether the file is an archive to work out what happens, and a URL download with ADD leaves the downloaded file sitting in its own layer, while a curl in a RUN step lets me check it, unpack it and clean up in the same layer. So COPY for normal files, ADD only when I actually want a local tarball extracted."

Red flag to avoid:

Saying there's no difference, or using ADD everywhere without knowing it unpacks archives.

They may ask next:
  • If ADD downloads a tar file from a URL, does it get unpacked?
  • Why can a download in a RUN step leave a smaller image than ADD with a URL?
Say it in 60 seconds

Storage & Networking 4 questions

Easy Technical round Fresher, Mid-level Practice question

13. I removed my database container and all the data was gone. Explain why, and compare volumes, bind mounts and tmpfs.

What the interviewer is really testing:
Whether you know the writable layer dies with the container and can pick the right kind of storage for data, development and scratch files.
Answer frame:

Writable layer: anything written inside the container is deleted with docker rm.

Volume: managed by Docker, outlives the container, the normal choice for database data.

Bind mount and tmpfs: a bind mount maps a host path, good for local code and config; tmpfs lives in memory and is never written to disk.

Sample spoken answer:

"The data was written into the container's writable layer, and that layer is deleted when the container is removed. So the fix is to put the data directory on a volume. A named volume is storage that Docker creates and manages, separate from any container. I can remove and recreate the database container, point it at the same volume, and the data is still there. A bind mount maps a specific folder from the host into the container. I use those in development, so my code changes show up without a rebuild, and sometimes for a config file. The downside is it depends on the host's folder layout and file ownership, which often causes permission errors. tmpfs is a mount held in memory, handy for scratch files or something sensitive that should never touch disk."

Red flag to avoid:

Expecting data inside a container to survive docker rm, or using bind mounts to host paths for production database storage without thinking about it.

They may ask next:
  • Does docker compose down delete named volumes?
  • A bind-mounted folder gives permission denied inside the container. What's usually wrong?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

14. Explain Docker's network drivers: bridge, host, none and overlay. When would you use each one?

What the interviewer is really testing:
Whether you know what isolation each mode gives and can match a mode to a real need instead of defaulting to host networking to make things work.
Answer frame:

Bridge: the default; a private network on the host with outbound NAT, reached from outside through published ports.

Host: shares the host's network stack; no port mapping and no network isolation.

None and overlay: none gives only loopback; overlay spans several hosts for a cluster.

Sample spoken answer:

"Bridge is the default. Each container gets its own network namespace and a private IP on a virtual bridge on the host, traffic out goes through NAT, and anything coming in has to use a published port. I always create a user-defined bridge rather than use the default one, because it gives DNS by container name. Host mode drops the network isolation: the container uses the host's interfaces directly, so if the app listens on 8080 it's on the host's 8080. It saves a little overhead and helps with things that need lots of ports, but you lose isolation and can hit port clashes. None gives only a loopback interface, which suits a batch job that should have no network at all. Overlay connects containers across several hosts, which is what a Swarm cluster uses. There's also macvlan, when a container must look like its own device on the physical network."

Red flag to avoid:

Switching everything to host networking to fix connection problems, or not knowing the default bridge has no name-based DNS.

They may ask next:
  • Why does host networking behave differently on Docker Desktop than on a Linux server?
  • Two containers on different user-defined networks need to talk. What are your options?
Say it in 60 seconds
Medium Technical round Fresher, Mid-level Practice question

15. Your API container can't connect to Postgres at localhost:5432, even though Postgres is running in another container. Why, and how do you fix it?

What the interviewer is really testing:
Whether you understand that each container has its own network namespace, so localhost means itself, and that containers find each other by name on a shared network.
Answer frame:

Cause: inside a container, localhost is the container itself, not the host or its neighbours.

Fix: put both on one user-defined network and use the other container's name as the host.

Ports: containers talk on the internal port; published host ports are only for traffic from outside.

Sample spoken answer:

"Every container has its own network namespace, so localhost inside the API container points at the API container itself, and nothing is listening on 5432 there. The fix is to put both containers on the same user-defined network and connect using the database container's name, so the connection string says db:5432 instead of localhost. Docker runs an internal DNS on user-defined networks that resolves that name to the container's current IP. Compose does all of this for me: every service joins a shared network and the service name works as a hostname. Two related points. Containers talk to each other on the container port, so I don't need to publish 5432 to the host at all, which is also safer. And if a container genuinely needs something running on the host, Docker Desktop provides host.docker.internal, and on Linux I can map that name to the host gateway."

Red flag to avoid:

Hard-coding a container's IP address, or publishing the database port to the host just so another container can reach it.

They may ask next:
  • Why does the name lookup fail on the default bridge network?
  • The database container was recreated and got a new IP. Why doesn't the API break?
Say it in 60 seconds
Easy Technical round Fresher Practice question

16. What does EXPOSE actually do, and how is it different from publishing a port with -p?

What the interviewer is really testing:
Whether you know that EXPOSE is documentation and publishing is what opens a port, plus the common bind-address mistake.
Answer frame:

EXPOSE: records which port the app listens on; it opens nothing by itself.

-p host:container: maps a host port to a container port so traffic from outside gets in.

Gotcha: the app must listen on 0.0.0.0 inside the container, not 127.0.0.1.

Sample spoken answer:

"EXPOSE is basically documentation. It records in the image that the app listens on, say, port 3000, and tools can read that, but it doesn't make the port reachable from anywhere. Publishing is what does that: -p 8080:3000 tells Docker to forward port 8080 on the host to 3000 in the container. There's also -P, which publishes every exposed port to a random high port on the host. The mistake I see most often is an app that listens on 127.0.0.1 inside the container. The port is published correctly, but the app only accepts connections from inside its own namespace, so from the host you get connection refused. It needs to listen on 0.0.0.0. And if I only want the port on my own machine, I publish it as 127.0.0.1:8080:3000 so it isn't open to the whole network."

Red flag to avoid:

Thinking EXPOSE publishes the port, or missing the 127.0.0.1 bind problem inside the container.

They may ask next:
  • Do you need EXPOSE for two containers on the same network to talk to each other?
  • Why might a published port be reachable from the internet even though the server's firewall blocks it?
Say it in 60 seconds

Compose & Config 3 questions

Easy Technical round Fresher, Mid-level Practice question

17. What problem does Docker Compose solve, and what goes into a compose file?

What the interviewer is really testing:
Whether you've used Compose to run a multi-container setup and know its building blocks, not just that it exists.
Answer frame:

Problem: replaces a pile of long docker run commands with one declared file for the whole stack.

Contents: services with their image or build, ports, environment, volumes and networks.

Commands: up to create and start everything, down to stop and remove it, logs and exec for day-to-day work.

Sample spoken answer:

"Without Compose, running an app with a database and a cache means three long docker run commands with the right networks, volumes and environment, typed in the right order. Compose lets me declare all of that in one YAML file that lives in the repo. Each service says either which image to use or how to build it, plus its ports, environment variables, volumes and dependencies. Compose creates a shared network automatically, so services reach each other by service name, and it creates named volumes too. Then docker compose up -d brings the whole stack up, docker compose logs follows the output, and docker compose down tears it down. The biggest win is that a new teammate clones the repo, runs one command and has the same setup as everyone else. I mostly use it for local development, CI test environments and small single-host deployments."

Red flag to avoid:

Describing Compose as a production cluster orchestrator, or not knowing services can reach each other by name.

They may ask next:
  • How would you keep one compose file for development but change a few settings for CI?
  • When would you stop using Compose and move to an orchestrator?
Say it in 60 seconds
Medium Technical round Mid-level Practice question

18. In Compose, the API starts before the database is ready and crashes, even with depends_on set. Why, and how do you fix it?

What the interviewer is really testing:
Whether you know depends_on only orders startup, and can wire a health check so a service waits for real readiness.
Answer frame:

Cause: plain depends_on waits for the container to start, not for the database to accept connections.

Fix: add a healthcheck to the database and use condition: service_healthy.

Still: the app should retry its connection, because databases restart in production too.

Sample spoken answer:

"In its short form, depends_on only controls start order. Compose starts the database container first and then the API, but a started container isn't a ready database. Postgres might still be initialising for a few seconds, so the API's first connection fails and it exits. The fix is to give the database a healthcheck, for Postgres that's pg_isready, and then use the long form of depends_on with condition: service_healthy. Now Compose holds the API back until the check passes. There's also service_completed_successfully, which is handy for a migrations job that must finish before the app starts. But I don't stop there. The app itself should retry the connection with a backoff, because in production the database can restart or fail over at any time, and a service that dies whenever the database blinks is fragile no matter how neatly it started."

Code:
services:
  api:
    build: .
    depends_on:
      db:
        condition: service_healthy
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: devonly
    volumes:
      - dbdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 10
volumes:
  dbdata:
Red flag to avoid:

Adding a fixed sleep before starting the app, or believing depends_on waits for the database to be ready.

They may ask next:
  • What's the difference between a container's restart policy and a health check?
  • How would you run database migrations before the API starts?
Say it in 60 seconds
Medium Technical round Mid-level, Senior Practice question

19. How do you pass configuration and secrets like a database password into a container without baking them into the image?

What the interviewer is really testing:
Whether you know where ENV, ARG and runtime values end up and can keep secrets out of images, history and logs, both at build time and at run time.
Answer frame:

Never in the image: ENV and ARG values are stored in the image config or history and anyone who pulls it can read them.

Runtime: plain config through environment variables; real secrets as files mounted from a secret store.

Build time: a BuildKit secret mount for things like a private registry token.

Sample spoken answer:

"First rule: nothing secret goes into the Dockerfile. An ENV value is saved in the image config, and ARG values used in a RUN step show up in docker history, so anyone who can pull the image can read them. For ordinary config, like a log level or a feature flag, environment variables passed at run time are fine: -e, an env file, or the environment block in Compose. For real secrets I prefer mounting them as files, from Compose secrets, an orchestrator's secret object or a cloud secret manager, because environment variables show up in docker inspect, get inherited by child processes and end up in crash dumps and debug logs. At build time, when I need a token to pull private packages, I use a BuildKit secret mount. The file is available for that one RUN step and never written into a layer."

Code:
# Dockerfile step:
# RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci

docker build --secret id=npmrc,src=.npmrc -t api:1.4.0 .
Red flag to avoid:

Putting passwords in ENV or ARG, or thinking a secret is safe because it's removed in a later layer.

They may ask next:
  • What's the difference between ARG and ENV?
  • Someone committed a secret in an ENV line and the image was pushed. What are your next steps?
Say it in 60 seconds

Debugging & Operations 5 questions

Medium Technical round Fresher, Mid-level Practice question

20. You start a container and it exits straight away. How do you find out why?

What the interviewer is really testing:
Whether you have a calm, ordered way to debug a dead container and know the common causes, not just rerunning it and hoping.
Answer frame:

Facts first: docker ps -a for the exit code, docker logs for the output, docker inspect for state and the command that ran.

Common causes: the main process finished, it daemonised into the background, a missing setting crashed it, or the entrypoint path is wrong.

Explore: run the image again with a shell as the entrypoint and try the command by hand.

Sample spoken answer:

"A container lives only as long as its main process, so the question is why that process ended. I run docker ps -a to see the exit code, then docker logs on the container, because the answer is usually printed there. Exit code 0 means the process finished normally: the command just ran and completed, or it's a server that daemonised itself into the background, like starting a service with an init script, so the foreground process returned. A non-zero code usually means a crash, often a missing environment variable or config file. 127 means the command wasn't found, and 126 means it was found but couldn't be executed. If the logs don't make it obvious, I run the same image with --entrypoint sh and -it, and try the command by hand to see what's there. A classic one is a script saved with Windows line endings."

Red flag to avoid:

Adding a sleep or tail -f /dev/null to keep the container alive without finding out why it exits.

They may ask next:
  • What does exit code 137 tell you?
  • The image has no shell at all. How do you look inside it?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

21. How do you limit a container's memory and CPU, and what happens when it goes over? What does exit code 137 mean?

What the interviewer is really testing:
Whether you know limits are off by default, that memory and CPU fail in different ways, and can read an out-of-memory kill from the evidence.
Answer frame:

Default: no limits, so one container can starve the whole host.

Setting them: --memory and --cpus on docker run, or resource limits in Compose; they become cgroup settings.

Over the limit: too much memory gets the process killed, exit 137 with OOMKilled in inspect; too much CPU is only throttled.

Sample spoken answer:

"By default a container has no limits, so a memory leak in one container can take down everything on the host. I set them with --memory and --cpus, or in the Compose file, and Docker turns them into cgroup settings. Memory and CPU behave very differently when you hit them. CPU over the limit just gets throttled, so the app slows down. Memory over the limit means the kernel's out-of-memory killer kills the process, and the container exits with 137. That's 128 plus 9, meaning it died from SIGKILL. To confirm it was memory, I check docker inspect for OOMKilled being true, and the host's kernel log. 137 can also mean docker stop gave up waiting and killed it, so I don't assume. Then I check that the runtime sizes its heap against the container limit, not the host's memory, and I look for a real leak."

Red flag to avoid:

Thinking a CPU limit kills the container, or treating every 137 as an out-of-memory kill without checking.

They may ask next:
  • What is exit code 143, and why is it usually a good sign?
  • A service is slow but never crashes after you set a CPU limit. How would you confirm it's being throttled?
Say it in 60 seconds
Hard Technical round Mid-level, Senior Practice question

22. An image you built on your laptop fails on the server with 'exec format error'. What's the likely cause and fix?

What the interviewer is really testing:
Whether you recognise a CPU architecture mismatch between an ARM laptop and an x86 server, and know how multi-platform builds solve it.
Answer frame:

Cause: the image holds binaries for one CPU architecture, for example arm64 from an ARM laptop, and the server is amd64.

Check: docker image inspect shows the image's architecture; compare it with the server.

Fix: build for the target with --platform, or build a multi-platform image with buildx and push both.

Sample spoken answer:

"Most of the time that's a CPU architecture mismatch. If I build on a laptop with an ARM chip, Docker builds an arm64 image by default. The server is usually x86, amd64, and the kernel can't execute arm64 binaries, so it fails with exec format error. I confirm it by inspecting the image and reading its architecture. The fix is to build for the platform I'm deploying to, with --platform linux/amd64, or better, use buildx to build for both architectures and push one multi-platform image. The registry then stores a list of images per platform, and each machine pulls the one that matches it. Building for another architecture on a laptop runs under emulation, which is slow, so in practice I let CI build on native runners. The same error can also come from a script with no shebang line, so I check that if the architecture matches."

Code:
docker image inspect --format '{{.Os}}/{{.Architecture}}' api:1.4.0

docker buildx build --platform linux/amd64,linux/arm64 \
  -t registry.example.com/api:1.4.0 --push .
Red flag to avoid:

Blaming a corrupt image and rebuilding the same way, or not knowing images are tied to a CPU architecture.

They may ask next:
  • What is a manifest list, and how does docker pull use it?
  • Why can a multi-platform build be very slow on one machine?
Say it in 60 seconds
Easy Situational round Fresher, Mid-level Practice question

23. Production is broken and a colleague suggests exec-ing into the running container to edit a config file and fix it now. What do you do?

What the interviewer is really testing:
Whether you know containers are meant to be replaceable and can balance urgency against drift, lost changes and an untraceable production state.
Answer frame:

Problem: edits in a container vanish on restart, apply to only one replica and leave no record.

First move: roll back to the last good image or change the config through the normal path.

If truly forced: do it with a witness, write it down, and follow up with a proper fix the same day.

Sample spoken answer:

"I'd push back, calmly, because it probably won't hold. Anything changed inside a running container is lost the moment it restarts or gets rescheduled, and if there are several replicas we'd only fix one. It also means production no longer matches any image or commit, so the next deploy quietly brings the bug back. My first move is to roll back to the last known good image tag, which is usually the fastest safe fix anyway. If it's config, I change it where config lives, the environment or the mounted config, and restart the containers properly. If none of that is possible and customers are hurting, I'd accept a manual edit as a stopgap, with a second person watching, noted in the incident channel, and a proper fix shipped through the pipeline the same day."

Red flag to avoid:

Editing the live container and calling it fixed, with no rollback option and no follow-up change in the image or config.

They may ask next:
  • What makes a rollback fast and safe in a container setup?
  • How would you stop people from being able to exec into production containers at all?
Say it in 60 seconds
Hard Situational round Senior Practice question

24. Your team wants to run the production database in a Docker container on the same host as the app. How do you weigh that decision?

What the interviewer is really testing:
Whether you can reason about stateful workloads honestly: what containers change, what they don't, and when a managed service is the better call.
Answer frame:

What's fine: a database in a container runs normally if its data lives on a volume on reliable storage.

What's hard: backups, restores, upgrades, performance tuning and failover are still your job.

Decide: team skill, the cost of downtime and data loss, and whether a managed database is simpler.

Sample spoken answer:

"I don't think containers make a database unsafe by themselves. Postgres in a container, with its data on a named volume on good storage, runs like Postgres anywhere else. What containers don't do is solve the hard parts. Someone still has to run backups and, more importantly, test restores. Someone has to plan version upgrades, tune memory against the container limit, watch disk, and handle failover if the host dies. Putting it on the same host as the app also means one bad machine takes out both, and the app can starve the database of memory unless limits are set. So I'd ask: does the team have the skill and time to operate a database, and what does an hour of downtime or a lost day of data cost us? For a small team, a managed database usually wins. Containers stay perfect for development and test databases."

Red flag to avoid:

A flat 'never run databases in containers' or 'it's fine, just add a volume', with no mention of backups, restores or failure of the host.

They may ask next:
  • If you did run it in a container, what would your backup and restore plan look like?
  • What would make you move from a containerised database to a managed one later?
Say it in 60 seconds

Security & Registries 3 questions

Medium Technical round Mid-level, Senior Practice question

25. What do you do to make a Docker image and the container running it more secure?

What the interviewer is really testing:
Whether you have a practical, layered checklist covering the image, the build and the runtime settings, not just 'scan it'.
Answer frame:

Image: small pinned base, only needed packages, multi-stage builds, no secrets in layers, rebuilt often for patches.

Runtime: a non-root user, a read-only root filesystem, dropped capabilities, no --privileged, no Docker socket mount.

Process: scan in CI, pin by version or digest, and pull only from trusted registries.

Sample spoken answer:

"I think about it in three layers. For the image itself: a small, pinned base, only the packages the app needs, a multi-stage build so compilers don't ship, and no secrets in any layer. I rebuild regularly, because most fixes arrive through updated base images. For the runtime: a USER instruction so the process isn't root, a read-only root filesystem with a tmpfs for the few paths that need writing, all Linux capabilities dropped and only the needed ones added back, and never --privileged. I also never mount the Docker socket into an app container, because control of that socket is effectively root on the host. For the process: an image scanner runs in CI and fails the build on serious findings with a fix available, deployments pin a version or digest, and we pull only from registries we trust."

Code:
docker run -d --name api \
  --user 10001 --read-only --tmpfs /tmp \
  --cap-drop ALL --security-opt no-new-privileges \
  -p 8080:3000 registry.example.com/api:1.4.0
Red flag to avoid:

Saying containers are secure by default because they're isolated, or running everything as root and privileged to avoid permission errors.

They may ask next:
  • Why is root inside a container still a risk if the container is isolated?
  • Your app needs to bind to port 80. How do you allow that without running as root?
Say it in 60 seconds
Medium Technical round Fresher, Mid-level Practice question

26. How do registries, tags and digests work? Why is deploying the latest tag to production risky?

What the interviewer is really testing:
Whether you know tags are movable labels and digests are fixed, and can set up a tagging scheme that makes deployments repeatable and rollbacks easy.
Answer frame:

Registry: stores images in repositories; you tag, log in, push and pull.

Tag vs digest: a tag is a movable name; a digest is a content hash that always means the same bytes.

latest: just the default tag name, not the newest build; deploy an explicit version or a digest instead.

Sample spoken answer:

"A registry is a server that stores images, organised into repositories. I tag an image with the registry address, repository and a tag, log in, and push, and servers pull by the same name. A tag is just a pointer that can be moved to a different image at any time. A digest is a hash of the image content, so a given digest always means exactly the same bytes. latest isn't magic. It's simply the tag you get when you don't give one, and it doesn't mean the newest build. Deploying latest is risky because two servers can pull it at different times and run different code, and a rollback has nothing to point back to. I tag each build with the version or git commit, deploy that tag or its digest, and turn on immutable tags in the registry where I can."

Red flag to avoid:

Believing latest always points to the newest image, or deploying without being able to say exactly which image is running.

They may ask next:
  • How would you roll back a bad deployment with this tagging scheme?
  • What's the trade-off of pinning a base image by digest?
Say it in 60 seconds
Medium Situational round Mid-level, Senior Practice question

27. The night before a release, the image scanner reports dozens of critical vulnerabilities in your base image. How do you handle it?

What the interviewer is really testing:
Whether you triage security findings with judgement, neither ignoring them nor blocking a release blindly, and fix the cause rather than the report.
Answer frame:

Triage: which findings have a fix available, and which packages the app actually uses or exposes.

Quick wins: rebuild on the patched base version, or move to a slimmer base that drops unused packages.

Decide and record: ship or hold with the security owner, and log any accepted risk with an owner and a date.

Sample spoken answer:

"First I'd triage instead of panicking. A long list against a base image is common, and the real questions are which ones have a fix available and which touch something the app actually uses or exposes. Often simply rebuilding on the latest patch version of the same base clears most of them, and that's a low-risk change I can test tonight. If many findings are in packages the app never needs, moving to a slim or distroless base removes them for good, but that's a bigger change I wouldn't rush the night before unless tests are strong. For anything left, I'd sit down with whoever owns security and decide together: ship with a documented, time-boxed exception, or hold the release if something is exploitable in our setup. Afterwards I'd add scheduled rebuilds so we don't find these the night before again."

Red flag to avoid:

Either ignoring the report because the app works, or blocking the release without checking whether any finding matters or has a fix.

They may ask next:
  • How do you decide whether a vulnerability is actually reachable in your application?
  • What policy would you set so CI fails on scan results without blocking every build?
Say it in 60 seconds

Real Work 3 questions

Medium Behavioral round Mid-level, Senior Practice question

28. Tell me about an application you moved into Docker. What had to change in the app, and what surprised you?

What the interviewer is really testing:
Whether you've done real containerisation work and learned that the app often has to change, around config, logs, state and dependencies, not just get wrapped in a Dockerfile.
Answer frame:

Starting point: what the app was and how it used to run.

Changes: config into environment variables, logs to stdout, state moved out of the container.

Surprise and result: the thing you didn't expect, and what the move bought the team.

Sample spoken answer:

"At my last company I moved a Python reporting service off a hand-built server and into Docker. Writing the Dockerfile took an afternoon. Making the app fit took two weeks. It read config from a file with the database host hard-coded, so I moved every setting to environment variables. It wrote logs to a file on disk, which vanishes with the container, so I switched logging to stdout and let the platform collect it. It also saved uploaded files locally, so I moved those to object storage, which made it stateless and let us run two copies. The surprise was PDFs: on the slim base image they rendered with the wrong fonts, because the old server had fonts installed years ago that nobody remembered. I added the font package explicitly. After the move, a new environment went from a day of setup to one command."

Red flag to avoid:

A story where the only work was writing a Dockerfile, with no change to config, logs or state.

They may ask next:
  • How did you test that the containerised version behaved the same as the old server?
  • What would you do differently if you did it again?
Say it in 60 seconds
Hard Behavioral round Mid-level, Senior Practice question

29. Describe a production problem you traced back to how containers were configured or run. How did you find it?

What the interviewer is really testing:
Whether you can debug from evidence across the container and the host, and whether you fixed the root cause and put a guard in place.
Answer frame:

Symptom: what users or monitoring saw, and how bad it was.

Investigation: the evidence you gathered and the wrong turns you ruled out.

Fix and guard: the root cause fix and what stops it from coming back.

Sample spoken answer:

"At my last company a host running about a dozen containers started failing every few weeks. Services would crash with odd write errors, and a restart fixed it for a while. I checked the containers first and found nothing, so I looked at the host and the disk was full. Running du under Docker's data directory showed one container's log file had grown to tens of gigabytes. Docker's default json-file logging driver doesn't rotate logs unless you tell it to, and one chatty service was logging every request at debug level. I set max-size and max-file log options in the daemon config so every container got rotation by default, turned that service back to info level, and cleaned up old images and stopped containers. Then I added a disk-usage alert on every Docker host, so we'd hear about it long before a crash."

Red flag to avoid:

A story that ends with 'we restarted it and it went away', with no root cause and no guard added.

They may ask next:
  • Would changing the daemon's log settings affect containers that were already running?
  • How do you decide what should log at debug level in production?
Say it in 60 seconds
Medium Behavioral round Fresher, Mid-level, Senior Practice question

30. Tell me about a time you made Docker builds faster or images smaller. What did you measure and what changed?

What the interviewer is really testing:
Whether you improve things by measuring first and can explain which specific change produced which gain.
Answer frame:

Baseline: the build time and image size before, and why it mattered.

Changes: each change and what it fixed: ordering, ignore file, multi-stage, CI cache.

Result: the numbers after and the effect on the team.

Sample spoken answer:

"At my first job we had a CI build that took around twelve minutes, even for a one-line change, and an image over a gigabyte. I looked at the build log first. Every build reinstalled all dependencies, because the Dockerfile copied the whole repo before the install step. I reordered it so the lock file went in first. Then I found the .git folder and test fixtures in the image because there was no .dockerignore, so I added one. I split the file into a build stage and a slim runtime stage, which dropped the compiler and dev tools. Last, since CI runners start empty, I set up BuildKit to store its cache in the registry. Normal builds went to about three minutes and the image to under two hundred megabytes, and deploys got noticeably quicker."

Red flag to avoid:

Listing tricks without any before and after numbers, or claiming a gain without knowing which change caused it.

They may ask next:
  • Which of those changes gave the biggest gain, and how do you know?
  • How would you stop the image from growing back over time?
Say it in 60 seconds
Were you asked something else? Share it A person checks every question before it goes on the site. No name is shown.
For the call itself

The questions above are the prep. The call has ten more.

ClapAssist is an AI interview assistant for Mac and Windows. It listens to the interview on your computer and shows you what to say, in short lines you can read while you talk. Your resume and notes are never stored on our servers. It stays out of screen share on every plan; only you can see it.

Download ClapAssist with 10 free minutes
Mac and Windows · Stays out of screen share · No card