Skip to content
Back to Interview Guides
Interview Guide

30 Advanced Docker Interview Questions for Experienced Developers

· 16 min read
Docker Q&A Component

Jump to Category

️ Dockerfile & Image Optimization ⚙️ Internals & Core Concepts
️ Networking Storage & Volumes
Docker Compose ️ Security

Dockerfile & Image Optimization

1. What is a multi-stage build and why is it a best practice?

A **multi-stage build** is a feature that allows you to use multiple `FROM` instructions in a single Dockerfile. Each `FROM` instruction begins a new “stage” of the build. You can selectively copy artifacts from one stage to another, discarding everything you don’t need in the final image.

It’s a best practice because it allows you to create lean, production-ready images. For example, you can use a first stage with a full build environment (like a JDK or Node.js image with all build tools) to compile your application. Then, in a second stage, you start from a minimal base image (like `alpine` or `distroless`) and copy *only* the compiled artifact (the JAR file or JavaScript bundle) into it. This results in a final image that is significantly smaller and more secure because it doesn’t contain any build tools or unnecessary dependencies.

Read the documentation on multi-stage builds.

2. How does the Docker build cache work, and how can you strategically bust the cache?

During an image build, Docker processes each instruction in the Dockerfile. For each instruction, it checks if it already has a cached layer from a previous build. If the instruction and the files it depends on are unchanged, Docker reuses the existing layer instead of re-executing the instruction. This makes builds much faster.

You can **bust the cache** (force re-execution) by changing an instruction or the files it depends on. A common strategy is to order your Dockerfile instructions from least to most frequently changing. For example:

  1. Copy your dependency manifest file (e.g., `package.json`, `pom.xml`).
  2. Run the dependency installation command (e.g., `npm install`). This layer is only rebuilt if the manifest file changes.
  3. Copy the rest of your application source code. This changes frequently, but the expensive dependency layer above it is preserved.
Learn more about the build cache.

3. What is the difference between the `COPY` and `ADD` instructions in a Dockerfile?

Both instructions copy files from a source into the container’s filesystem.

  • `COPY` is the more straightforward command. It simply copies local files or directories into the container.
  • `ADD` has two additional features: it can copy files from a remote URL, and it can automatically unpack compressed files (like `.tar.gz`) into the destination directory.

The official best practice is to always prefer **`COPY`** unless you specifically need `ADD`’s tar auto-extraction feature. This is because `COPY` is more transparent and predictable. Using `ADD` for remote URLs can add bloat and unpredictability to your image layers.

4. What are “distroless” images and what security benefit do they provide?

Distroless images are minimal container base images from Google that contain only your application and its runtime dependencies. They do **not** contain a package manager, shell, or other standard Linux utilities that you would find in a typical OS distribution like Alpine or Debian.

The primary benefit is **security**. By removing all non-essential components, you significantly reduce the attack surface of your container. If an attacker gains shell access to your container, they will find no tools (like `curl`, `wget`, or even `ls`) to use for reconnaissance or further attacks.

5. How can you minimize the number of layers in a Docker image?

Each `RUN`, `COPY`, and `ADD` instruction in a Dockerfile creates a new layer. To minimize layers, you should chain related commands together into a single `RUN` instruction using the `&&` operator. For example, instead of:

RUN apt-get update
RUN apt-get install -y curl

You should use:

RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

The final `rm` command also ensures that the package manager’s cache is cleaned up within the same layer, reducing the final image size.

6. What is the `ONBUILD` instruction used for?

The `ONBUILD` instruction adds a trigger instruction to an image that will be executed at a later time, when the image is used as the base for another build. For example, you could create a base Node.js image with `ONBUILD COPY . /app`. Any subsequent Dockerfile that uses `FROM my-node-image` will automatically have its source code copied into the `/app` directory.

It’s useful for creating generic base images that can be used for multiple similar projects without repeating the same boilerplate `COPY` and `RUN` commands in every child Dockerfile.

Internals & Core Concepts

7. How does Docker use Linux kernel features like namespaces and cgroups?

Docker containers are made possible by two key Linux kernel features:

  • Namespaces: Provide isolation. Each container gets its own isolated “view” of the system. For example, the `pid` namespace isolates process IDs (so the first process in a container thinks it’s PID 1), the `net` namespace provides an isolated network stack, and the `mnt` namespace isolates filesystem mount points.
  • Control Groups (cgroups): Limit and manage resource usage. Cgroups allow Docker to enforce limits on how much memory, CPU, and I/O a container can use, preventing a single container from consuming all the host’s resources.

A container is essentially a regular process on the host OS, but wrapped in these layers of isolation and resource constraints.

Read the Docker blog on container isolation.

8. What is the difference between `docker exec` and `docker attach`?

  • `docker attach`: Attaches your terminal’s standard input, output, and error streams to the main running process (PID 1) inside a container. It’s like “resuming” the container’s primary process. If you exit, the primary process might stop, terminating the container.
  • `docker exec`: Executes a *new* command inside an already running container. This creates a new process inside the container’s namespaces. It’s primarily used for debugging and inspecting a running container without disturbing its main process.

9. Explain how Docker’s image layering and Union File System work.

A Docker image is composed of a stack of read-only layers. Each instruction in a Dockerfile creates a new layer. When you create a container from an image, Docker adds a thin, writable layer on top of the read-only image layers. This is made possible by a **Union File System** (like OverlayFS).

The UnionFS merges these layers into a single, unified view. If you modify a file that exists in a lower read-only layer, the file system performs a “copy-on-write” operation, copying the file up to the writable layer before modifying it. This makes containers extremely efficient to create, as they don’t need to duplicate the entire image filesystem.

Read the documentation on Docker storage drivers.

10. What is the role of the Docker daemon (`dockerd`)?

The Docker daemon is a persistent background process that manages Docker objects. It listens for Docker API requests (from the Docker CLI or other clients) and is responsible for building, running, and distributing containers. It handles all the low-level tasks like creating networks, managing storage volumes, and interacting with the kernel to create the isolated container environments.

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

An **image** is a read-only, inert template that contains the application and all its dependencies. It’s a blueprint for creating containers, composed of a stack of layers.

A **container** is a runnable instance of an image. It is a live, running process with its own isolated filesystem, network, and process space. You can have many containers running from the same image.

12. What does the `docker prune` command do?

The `docker system prune` command is a utility for cleaning up unused Docker resources. By default, it removes all stopped containers, all networks not used by at least one container, and all dangling images (images without a tag that are not referenced by any container). You can add the `-a` flag to remove all unused images (not just dangling ones) and the `–volumes` flag to remove unused volumes. It’s a useful command for reclaiming disk space.

Networking

13. Explain the different default Docker network drivers: `bridge`, `host`, and `overlay`.

  • `bridge` (default): Creates a private, internal network on the host. Containers on the same bridge network can communicate with each other by their name. Docker provides DNS resolution between them. To expose a container to the outside world, you must map ports from the host to the container.
  • `host`: This driver removes network isolation between the container and the host. The container shares the host’s networking stack directly. For example, a web server running in the container on port 80 would be directly accessible on the host’s port 80.
  • `overlay`: A distributed network that can span multiple Docker hosts. It’s used to enable communication between containers running on different machines, which is essential for multi-host container orchestration with Docker Swarm.
Read the official Networking overview.

14. What is the difference between exposing a port and publishing a port?

  • `EXPOSE` (in a Dockerfile): A piece of metadata that documents which port a containerized application listens on. It does not actually open or map the port. It’s primarily informational for other developers or tools.
  • `–publish` or `-p` (in `docker run`): Actually creates a firewall rule that maps a port on the host machine to a port inside the container. This is what makes a container’s service accessible from outside the host.

15. How does DNS resolution work within a user-defined bridge network?

Docker runs an embedded DNS server on its bridge networks. When a container tries to connect to another container by its name (e.g., `ping database`), the request is intercepted by this DNS server. The server looks up the container name in its internal registry and resolves it to the correct internal IP address of that container on the bridge network. This allows for easy service discovery between containers in the same network.

16. What is a `macvlan` network?

A `macvlan` network allows you to assign a MAC address to a container, making it appear as a physical device directly on your physical network. The container gets its own IP address from the local network’s DHCP server. This is useful for legacy applications that expect to be directly connected to the physical network or for monitoring tools that need to inspect network traffic at a low level.

Storage & Volumes

17. Compare Docker volumes and bind mounts. When would you use each?

  • Volumes: Are fully managed by Docker. They are stored in a dedicated area on the host filesystem (`/var/lib/docker/volumes/`) and are decoupled from the host’s file structure. This is the recommended way to persist data generated by containers. They are more portable and can be managed via the Docker CLI.
  • Bind Mounts: Map a file or directory from the host machine directly into a container. The path on the host is arbitrary. They are useful for sharing configuration files from the host to the container or for development workflows where you want to reflect source code changes into a running container immediately.

Use **volumes** for all production data persistence. Use **bind mounts** primarily for local development or when you need to provide host-specific files to a container.

Read the documentation on Docker Storage.

18. What is a `tmpfs` mount?

A `tmpfs` mount creates a temporary filesystem that resides only in the host’s memory. It is not written to the host’s disk. When the container stops, the `tmpfs` mount is removed, and all data within it is gone. It’s useful for storing temporary, non-persistent state or sensitive data that you do not want to be written to disk for performance or security reasons.

19. What are orphaned volumes and how do you clean them up?

An orphaned (or dangling) volume is a Docker volume that is no longer associated with any container. They can accumulate over time as you create and destroy containers. You can clean them up to reclaim disk space using the command: `docker volume prune`.

Docker Compose

20. What is the difference between `depends_on` and a service healthcheck for controlling startup order?

`depends_on` only controls the *order* in which services are started. It will start the dependent service, but it does **not** wait for that service to be “ready” or “healthy” before starting the next one. For example, it might start a database container, and then immediately start the application container, which might crash if it can’t connect to the database yet.

A **`healthcheck`** is a much more robust solution. You define a command that Docker can run to check if a service is actually healthy (e.g., if a database is ready to accept connections). You can then configure `depends_on` with a `condition: service_healthy` to make sure Docker Compose waits for the dependency to pass its health check before starting the next service.

Read about controlling startup order in Compose.

21. How can you share common configurations between multiple Compose files?

You can use multiple Compose files and merge them. A common pattern is to have a base `docker-compose.yml` with the core service definitions. You can then have an override file, like `docker-compose.override.yml`, which Docker Compose automatically picks up to add or override configurations for local development (e.g., adding bind mounts and exposing ports).

For other environments, you can explicitly specify multiple files with the `-f` flag, like `docker-compose -f docker-compose.yml -f docker-compose.prod.yml up`.

22. What is the purpose of the `.env` file in Docker Compose?

The `.env` file is used to define environment variables that can be substituted into your `docker-compose.yml` file. Docker Compose automatically looks for a file named `.env` in the project directory and makes the variables defined within it available. This is useful for managing environment-specific configurations (like image tags or external ports) without hardcoding them into the Compose file itself.

23. How do you scale a service using Docker Compose?

You can scale a service to run multiple instances (containers) using the `docker-compose up –scale` command. For example, `docker-compose up –scale web=3` would start three instances of the `web` service. Docker Compose will automatically load balance requests between these instances on the bridge network.

Security

24. What is rootless mode and what security problem does it solve?

Rootless mode** allows you to run the Docker daemon and containers as a non-root user. This significantly mitigates the potential damage from a container breakout vulnerability. In a standard Docker setup, the daemon runs as root, so if an attacker escapes a container, they could gain root privileges on the host machine. In rootless mode, an escaped attacker would only gain the privileges of the unprivileged user running the daemon, greatly limiting their ability to harm the host system.

Learn more about Rootless mode.

25. How can you limit a container’s capabilities?

By default, Docker grants containers a limited set of Linux capabilities, but you can further restrict them for better security. The best practice is to drop all capabilities with `–cap-drop=ALL` and then add back only the specific ones your application absolutely needs with `–cap-add`. For example, a web server might not need any capabilities, while a network utility might need `NET_RAW`.

26. How do you scan a Docker image for known vulnerabilities?

You should integrate an image scanner into your CI/CD pipeline. Popular tools include:

  • Docker Scout: A service from Docker that provides detailed vulnerability analysis and remediation advice, integrated with Docker Hub and Docker Desktop.
  • Trivy: A popular open-source scanner that is simple to use and can be easily added to a CI pipeline.
  • Snyk: A developer security platform that provides container scanning as part of its broader set of tools.

27. What is the best practice for managing secrets in Docker?

The worst practice is to put secrets in the Dockerfile or as plain environment variables, as they can be easily exposed via `docker inspect`. The recommended approach is to use an orchestration tool’s secret management feature:

  • Docker Swarm: Use `docker secret`. The secret is encrypted and mounted into the container as a file in `/run/secrets/`.
  • Kubernetes: Use Kubernetes `Secrets`.
  • Docker Compose: For production, you would typically integrate with an external secrets manager like HashiCorp Vault or AWS Secrets Manager. The application inside the container would then be responsible for fetching the secret at runtime.

28. What does the `USER` instruction in a Dockerfile do?

The `USER` instruction sets the user name (or UID) to use when running the image and for any subsequent `RUN`, `CMD`, and `ENTRYPOINT` instructions. It’s a critical security best practice to create a non-root user in your Dockerfile and switch to it with the `USER` instruction. Running your application as a non-root user inside the container provides an important layer of defense, limiting the potential damage if the application process is compromised.

29. What is a “dangling” image?

A dangling image is an image layer that is not tagged and is not referenced by any other image. They most often occur when you build a new version of an image using the same tag. The old image loses its tag but still exists on the host. They are safe to remove and can be cleaned up with `docker image prune`.

30. What is the difference between `CMD` and `ENTRYPOINT`?

Both specify the command to be executed when a container starts, but they are used differently.

  • `ENTRYPOINT`: Configures a container that will run as an executable. It is not easily overridden. For example, `ENTRYPOINT [“/usr/bin/my-app”]`.
  • `CMD`: Provides default arguments for the `ENTRYPOINT`. These arguments *can* be easily overridden by providing a command when running the container. For example, if you have `CMD [“–help”]`, running `docker run my-image –version` would replace `–help` with `–version`.

The best practice is to use them together, with `ENTRYPOINT` specifying the executable and `CMD` specifying the default parameter.

Skip the interview marathon.

We pre-vet senior engineers across Asia using these exact questions and more. Get matched in 24 hours, $0 upfront.

Get Pre-Vetted Talent
WhatsApp