DEV Community

# Docker Interview Questions: A Hands-On Tutorial for Developers

Docker Interview Questions: A Hands-On Tutorial for Developers

Docker interview preparation becomes much easier when you stop memorizing isolated definitions and start building small, working examples. In this tutorial, you’ll create a containerized Python API, connect it to Redis with Docker Compose, inspect its networking and storage, and practice answering common interview questions using real commands.

Docker interview questions tutorial for developers featuring containerization, Dockerfiles, Docker Compose, Redis, networking, image optimization, CI/CD, and troubleshooting on a developer’s laptop.
If you are preparing for a DevOps role or planning to enroll in an AWS DevOps course in Bangalore, Docker interview questions are an important part of your preparation. Docker is widely used in CI/CD, cloud deployments, microservices, and infrastructure automation.

The examples use standard Docker workflows: images, containers, Dockerfiles, volumes, networks, Compose, caching, and troubleshooting. Docker’s official learning examples cover the same progression from running containers to building images, using volumes, networking services, Compose, and multi-stage builds. github

1. Prepare the Example Project

Create a new project:

mkdir docker-interview-lab
cd docker-interview-lab
Enter fullscreen mode Exit fullscreen mode

Create this structure:

docker-interview-lab/
├── app.py
├── requirements.txt
├── Dockerfile
├── .dockerignore
└── compose.yaml
Enter fullscreen mode Exit fullscreen mode

Create a small API

Add the following to app.py:

from flask import Flask, jsonify
import os
import socket

app = Flask(__name__)

@app.get("/")
def home():
    return jsonify({
        "message": "Docker interview lab is running",
        "hostname": socket.gethostname(),
        "environment": os.getenv("APP_ENV", "development")
    })

@app.get("/health")
def health():
    return jsonify({"status": "healthy"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
Enter fullscreen mode Exit fullscreen mode

The important detail is:

app.run(host="0.0.0.0", port=5000)
Enter fullscreen mode Exit fullscreen mode

If the application listens only on 127.0.0.1, it may work inside the container but remain inaccessible from your host machine.

Add this to requirements.txt:

Flask==3.1.0
Enter fullscreen mode Exit fullscreen mode

Create .dockerignore:

__pycache__/
*.pyc
.git/
.env
.venv/
venv/
Enter fullscreen mode Exit fullscreen mode

A .dockerignore prevents unnecessary files from being sent to the Docker build context. This makes builds faster and helps avoid accidentally copying local secrets or development files into the image.

2. Question: What Is Docker?

Docker is a platform for packaging and running applications in isolated containers.

A container includes the application and its required runtime dependencies, libraries, configuration, and filesystem changes. Unlike a virtual machine, a container does not usually include a complete guest operating system kernel. Containers share the host kernel while remaining isolated through operating-system features.

A useful interview answer is:

Docker packages an application and its dependencies into a portable image. A container is a running instance of that image, isolated from other processes but sharing the host operating system kernel.

Container versus virtual machine

Area Container Virtual machine
Operating system Shares the host kernel Includes a guest OS
Startup time Usually very fast Usually slower
Resource usage Generally lower Generally higher
Isolation Process-level isolation Hardware or hypervisor-level isolation
Packaging unit Image VM disk image
Typical use Services, CI, development Stronger isolation, different kernels, legacy workloads

Containers are not automatically equivalent to virtual machines. If an interviewer asks about security isolation, explain that the isolation boundary and threat model differ.

3. Question: What Is the Difference Between an Image and a Container?

An image is an immutable template containing application files, dependencies, metadata, and filesystem layers.

A container is a runnable instance of an image. When Docker starts a container, it adds a writable layer on top of the image’s read-only layers.

Try it:

docker pull python:3.12-slim
docker images
Enter fullscreen mode Exit fullscreen mode

Run an interactive Python container:

docker run --rm -it python:3.12-slim python
Enter fullscreen mode Exit fullscreen mode

Inside the Python prompt:

print("Running inside a container")
Enter fullscreen mode Exit fullscreen mode

Exit:

exit()
Enter fullscreen mode Exit fullscreen mode

The --rm flag removes the container after it stops.

Now run a named container in the background:

docker run -d \
  --name interview-python \
  python:3.12-slim \
  sleep 300
Enter fullscreen mode Exit fullscreen mode

Inspect it:

docker ps
docker inspect interview-python
Enter fullscreen mode Exit fullscreen mode

Stop and remove it:

docker stop interview-python
docker rm interview-python
Enter fullscreen mode Exit fullscreen mode

Useful distinction:

  • docker ps shows running containers.
  • docker ps -a shows running and stopped containers.
  • docker images lists local images.
  • docker inspect displays low-level metadata.
  • docker logs displays a container’s standard output and error streams.

4. Question: What Happens During docker run?

Consider this command:

docker run -d --name web -p 8080:5000 my-api:1.0
Enter fullscreen mode Exit fullscreen mode

Docker generally performs these steps:

  1. Checks whether the image exists locally.
  2. Pulls the image if it is unavailable locally.
  3. Creates a container from the image.
  4. Adds a writable container layer.
  5. Configures networking.
  6. Maps host port 8080 to container port 5000.
  7. Starts the container’s configured process.

The port format is:

host_port:container_port
Enter fullscreen mode Exit fullscreen mode

Therefore:

-p 8080:5000
Enter fullscreen mode Exit fullscreen mode

means that requests to port 8080 on your machine are forwarded to port 5000 inside the container.

5. Build the API Image

Add this to Dockerfile:

FROM python:3.12-slim

WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .

EXPOSE 5000

CMD ["python", "app.py"]
Enter fullscreen mode Exit fullscreen mode

Build the image:

docker build -t docker-interview-api:1.0 .
Enter fullscreen mode Exit fullscreen mode

Run it:

docker run -d \
  --name docker-api \
  -p 8080:5000 \
  -e APP_ENV=container \
  docker-interview-api:1.0
Enter fullscreen mode Exit fullscreen mode

Test it:

curl http://localhost:8080/
curl http://localhost:8080/health
Enter fullscreen mode Exit fullscreen mode

View logs:

docker logs docker-api
Enter fullscreen mode Exit fullscreen mode

Follow logs continuously:

docker logs -f docker-api
Enter fullscreen mode Exit fullscreen mode

Enter the running container:

docker exec -it docker-api sh
Enter fullscreen mode Exit fullscreen mode

Inside the container:

pwd
ls
python --version
Enter fullscreen mode Exit fullscreen mode

Leave the shell:

exit
Enter fullscreen mode Exit fullscreen mode

Clean up:

docker stop docker-api
docker rm docker-api
Enter fullscreen mode Exit fullscreen mode

6. Question: Explain the Main Dockerfile Instructions

FROM

Defines the base image:

FROM python:3.12-slim
Enter fullscreen mode Exit fullscreen mode

Use a trusted and maintained base image. Docker’s build guidance recommends choosing trusted images, keeping images small, and rebuilding regularly so dependencies and base-image security fixes are included. docs.docker

WORKDIR

Sets the working directory for subsequent instructions:

WORKDIR /app
Enter fullscreen mode Exit fullscreen mode

It is preferable to using repeated RUN cd /app commands because WORKDIR applies consistently to future instructions and the container’s default process.

COPY

Copies files from the build context into the image:

COPY requirements.txt .
COPY app.py .
Enter fullscreen mode Exit fullscreen mode

RUN

Executes a command while building the image:

RUN pip install --no-cache-dir -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

The result becomes part of an image layer.

EXPOSE

Documents the port that the application expects to use:

EXPOSE 5000
Enter fullscreen mode Exit fullscreen mode

It does not publish the port to the host. You still need:

-p 8080:5000
Enter fullscreen mode Exit fullscreen mode

CMD

Defines the default command:

CMD ["python", "app.py"]
Enter fullscreen mode Exit fullscreen mode

ENTRYPOINT

Defines the main executable for the container. For example:

ENTRYPOINT ["python"]
CMD ["app.py"]
Enter fullscreen mode Exit fullscreen mode

The resulting default command is equivalent to:

python app.py
Enter fullscreen mode Exit fullscreen mode

A common interview distinction is that CMD supplies defaults that can be replaced, while ENTRYPOINT is intended to define the executable. The exact behavior also depends on whether shell or exec form is used.

Prefer exec form:

CMD ["python", "app.py"]
Enter fullscreen mode Exit fullscreen mode

It handles signals more reliably than:

CMD python app.py
Enter fullscreen mode Exit fullscreen mode

7. Question: How Does Docker Image Layer Caching Work?

Each Dockerfile instruction can create a layer. Docker reuses a cached layer when the instruction and its relevant inputs have not changed.

This Dockerfile is cache-friendly:

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .
Enter fullscreen mode Exit fullscreen mode

If only app.py changes, Docker can reuse the dependency-installation layer.

This version is less efficient:

COPY . .

RUN pip install --no-cache-dir -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

Any source-code change can invalidate the COPY . . layer and force the dependency installation to run again.

Build with plain progress output:

docker build --progress=plain -t docker-interview-api:1.1 .
Enter fullscreen mode Exit fullscreen mode

Inspect image size:

docker image ls docker-interview-api
docker history docker-interview-api:1.1
Enter fullscreen mode Exit fullscreen mode

Performance tips:

  • Put stable instructions before frequently changing instructions.
  • Use .dockerignore.
  • Avoid copying the entire repository when only a few files are required.
  • Combine related package-installation commands carefully.
  • Remove package-manager caches where appropriate.
  • Use BuildKit cache mounts for package managers in larger projects.
  • Avoid rebuilding with --no-cache unless you specifically need a clean build.

8. Question: What Are Volumes and Bind Mounts?

Container filesystems are ephemeral. If a container is removed, data written only inside its writable layer disappears.

Named volume

Create a volume:

docker volume create app-data
Enter fullscreen mode Exit fullscreen mode

Use it:

docker run --rm \
  -v app-data:/data \
  alpine \
  sh -c "echo persistent-data > /data/example.txt"
Enter fullscreen mode Exit fullscreen mode

Read it from another container:

docker run --rm \
  -v app-data:/data \
  alpine \
  cat /data/example.txt
Enter fullscreen mode Exit fullscreen mode

The data survives the first container because it is stored in the named volume.

Bind mount

A bind mount maps a host directory into a container:

docker run --rm \
  -v "$(pwd)":/workspace \
  alpine \
  ls /workspace
Enter fullscreen mode Exit fullscreen mode

Bind mounts are useful during development because changes made on the host are immediately visible inside the container.

When should you use each?

Storage type Best use
Named volume Database data and Docker-managed persistent application data
Bind mount Local development, source-code editing, configuration injection
Image layer Static application files that do not need runtime persistence
External storage Production data requiring independent durability and backup

Do not treat a container’s writable layer as a database backup strategy.

9. Question: How Does Docker Networking Work?

Docker provides isolated networks that allow containers to communicate.

Create a network:

docker network create interview-net
Enter fullscreen mode Exit fullscreen mode

Start a container on it:

docker run -d \
  --name api \
  --network interview-net \
  docker-interview-api:1.0
Enter fullscreen mode Exit fullscreen mode

Run a temporary troubleshooting container on the same network:

docker run --rm \
  --network interview-net \
  alpine \
  ping -c 3 api
Enter fullscreen mode Exit fullscreen mode

On a user-defined Docker network, containers can typically reach one another by container name. This is why application configuration should use a service name such as redis, not localhost.

Inside a container:

  • localhost means the current container.
  • The host machine is a different network namespace.
  • Another container should be reached through its service or container name.

Inspect the network:

docker network inspect interview-net
Enter fullscreen mode Exit fullscreen mode

Remove it after testing:

docker stop api
docker rm api
docker network rm interview-net
Enter fullscreen mode Exit fullscreen mode

10. Add Redis with Docker Compose

Docker Compose is useful when an application consists of multiple services.

Create compose.yaml:

services:
  api:
    build:
      context: .
    container_name: interview-api
    ports:
      - "8080:5000"
    environment:
      APP_ENV: compose
      REDIS_HOST: redis
    depends_on:
      - redis

  redis:
    image: redis:7-alpine
    container_name: interview-redis
    volumes:
      - redis-data:/data

volumes:
  redis-data:
Enter fullscreen mode Exit fullscreen mode

Start both services:

docker compose up --build
Enter fullscreen mode Exit fullscreen mode

Run in the background:

docker compose up -d --build
Enter fullscreen mode Exit fullscreen mode

View service status:

docker compose ps
Enter fullscreen mode Exit fullscreen mode

View logs:

docker compose logs -f api
Enter fullscreen mode Exit fullscreen mode

Stop the services:

docker compose down
Enter fullscreen mode Exit fullscreen mode

Stop them and remove the named volume:

docker compose down -v
Enter fullscreen mode Exit fullscreen mode

Be careful with -v: it deletes the Compose-managed volume and its stored data.

A common Compose interview question

Does depends_on mean the dependency is ready?

Not necessarily. It controls startup order, but a service may still be initializing when the dependent application starts. Production applications should implement retries, connection timeouts, and health-aware startup behavior.

A stronger Compose configuration can add a health check:

services:
  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

  api:
    build: .
    depends_on:
      redis:
        condition: service_healthy
Enter fullscreen mode Exit fullscreen mode

Your application should still handle temporary connection failures gracefully.

11. Question: What Is a Multi-Stage Build?

A multi-stage build separates compilation or dependency-building tools from the final runtime image.

For a Python application, a simple example is:

FROM python:3.12-slim AS builder

WORKDIR /build

COPY requirements.txt .

RUN pip install \
    --no-cache-dir \
    --prefix=/install \
    -r requirements.txt


FROM python:3.12-slim

WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

COPY --from=builder /install /usr/local
COPY app.py .

EXPOSE 5000

CMD ["python", "app.py"]
Enter fullscreen mode Exit fullscreen mode

Build it:

docker build -t docker-interview-api:multi-stage .
Enter fullscreen mode Exit fullscreen mode

The final image does not need the builder’s temporary working files. This can reduce image size and attack surface. Docker’s official starter material includes multi-stage builds as a method for separating build-time and runtime dependencies. github

Multi-stage builds are especially useful for:

  • Go binaries.
  • Java applications.
  • Node.js front-end bundles.
  • TypeScript applications.
  • Applications with native compilation dependencies.

12. Question: Why Should Containers Avoid Running as Root?

Running as root inside a container can increase the impact of an application compromise. It does not solve every container-security concern, but using a non-root user is a practical hardening step.

Update the Dockerfile:

FROM python:3.12-slim

WORKDIR /app

RUN useradd --create-home appuser

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY --chown=appuser:appuser app.py .

USER appuser

EXPOSE 5000

CMD ["python", "app.py"]
Enter fullscreen mode Exit fullscreen mode

Check the user:

docker build -t docker-interview-api:secure .
docker run --rm docker-interview-api:secure id
Enter fullscreen mode Exit fullscreen mode

Other security practices include:

  • Do not place passwords or API keys in a Dockerfile.
  • Do not commit .env files containing credentials.
  • Use secret-management features appropriate to your deployment platform.
  • Avoid --privileged unless there is a clearly justified requirement.
  • Expose only necessary ports.
  • Use specific image versions rather than relying blindly on latest.
  • Scan images in CI and rebuild them regularly.
  • Use minimal runtime images where compatibility allows.

13. Practical GitHub Workflow

Put the project under version control:

git init
git add .
git commit -m "Add Docker interview lab"
Enter fullscreen mode Exit fullscreen mode

Create a repository on GitHub, then connect and push it:

git branch -M main
git remote add origin YOUR_REPOSITORY_ADDRESS
git push -u origin main
Enter fullscreen mode Exit fullscreen mode

Do not copy credentials into the repository. Your .dockerignore should exclude local environments and secret files, but review the repository before pushing:

git status
git diff --cached
Enter fullscreen mode Exit fullscreen mode

A basic GitHub Actions workflow can build the image on every push. Create .github/workflows/docker.yml:

name: Docker Build

on:
  push:
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Check out source
        uses: actions/checkout@v4

      - name: Build image
        run: docker build -t docker-interview-api:test .
Enter fullscreen mode Exit fullscreen mode

This does not publish an image. It simply confirms that the Dockerfile can build in a clean environment.

For a real project, add:

  • Unit tests.
  • Image vulnerability scanning.
  • Image tagging based on commit SHA.
  • Registry authentication through GitHub Secrets.
  • Deployment only after tests and security checks pass.

Docker maintains official example repositories covering Compose, sample applications, CI, image caching, and multi-stage builds. github

14. Troubleshooting Guide

Error: “Cannot connect to the Docker daemon”

Check whether Docker Desktop or the Docker service is running:

docker version
Enter fullscreen mode Exit fullscreen mode

On Linux systems using systemd:

sudo systemctl status docker
sudo systemctl start docker
Enter fullscreen mode Exit fullscreen mode

If permissions are the problem, your user may not have access to the Docker socket.

Error: “Port is already allocated”

Find the process using the port:

docker ps
Enter fullscreen mode Exit fullscreen mode

Change the host-side port:

docker run -p 8081:5000 docker-interview-api:1.0
Enter fullscreen mode Exit fullscreen mode

The application still listens on port 5000 inside the container; only the host port changed.

The container exits immediately

A container stays alive only while its main process is running.

Check its status and logs:

docker ps -a
docker logs container_name
Enter fullscreen mode Exit fullscreen mode

Do not use a long-running command merely to hide application failures. Identify why the main process stopped.

The API is unreachable

Check these items:

docker ps
docker logs docker-api
docker port docker-api
Enter fullscreen mode Exit fullscreen mode

Also verify that:

  • The application listens on 0.0.0.0.
  • The correct port mapping is used.
  • The container is running.
  • A host firewall is not blocking the port.

localhost does not reach Redis

From the API container, localhost refers to the API container itself. In Compose, use:

REDIS_HOST=redis
Enter fullscreen mode Exit fullscreen mode

Do not use localhost unless Redis is running in the same container, which is usually a poor service-design choice.

Changes are not appearing

If the source is copied into the image, rebuild it:

docker compose up --build
Enter fullscreen mode Exit fullscreen mode

For development, mount the source directory:

services:
  api:
    build: .
    ports:
      - "8080:5000"
    volumes:
      - .:/app
Enter fullscreen mode Exit fullscreen mode

A bind mount can hide files that were copied into the image at the same path. This sometimes causes confusing behavior.

Permission denied on mounted files

The host user ID and container user ID may differ. Check:

id
docker run --rm image_name id
Enter fullscreen mode Exit fullscreen mode

Choose a consistent development strategy rather than changing permissions broadly with unsafe commands.

15. Performance and Reliability Tips

Use these principles when answering practical Docker interview questions:

  • Keep the build context small with .dockerignore.
  • Order Dockerfile instructions to preserve cacheable layers.
  • Use multi-stage builds for compiled artifacts.
  • Select a suitable minimal base image, but test compatibility before switching to Alpine or distroless images.
  • Avoid installing development tools in the runtime image.
  • Use health checks for meaningful service readiness.
  • Set CPU and memory limits where appropriate.
  • Configure application workers based on workload rather than copying a default number.
  • Send logs to standard output and error so the runtime can collect them.
  • Add graceful shutdown handling for applications receiving termination signals.
  • Use persistent volumes or external storage for stateful data.
  • Tag releases immutably, for example with a version or commit SHA.
  • Rebuild images regularly to receive dependency and base-image updates.

An image that builds quickly but contains unnecessary packages, runs as root, stores secrets, or loses data on restart is not production-ready.

16. Practice Exercises

Try these without looking at the answers first.

Exercise 1: Inspect a container

Run:

docker run -d --name nginx-lab -p 8088:80 nginx:stable
Enter fullscreen mode Exit fullscreen mode

Then find:

  • The container IP address.
  • The image ID.
  • The published port.
  • The container’s main process.

Useful commands:

docker inspect nginx-lab
docker port nginx-lab
docker top nginx-lab
Enter fullscreen mode Exit fullscreen mode

Clean up:

docker rm -f nginx-lab
Enter fullscreen mode Exit fullscreen mode

Exercise 2: Prove volume persistence

  1. Create a named volume.
  2. Write a file from one container.
  3. Remove that container.
  4. Read the file from another container.

Explain why the file remains after the container is deleted.

Exercise 3: Break and fix networking

  1. Start Redis with Compose.
  2. Configure the API to use localhost.
  3. Observe the connection failure.
  4. Change the hostname to redis.
  5. Explain the difference between container-local and Compose-network addressing.

Exercise 4: Improve the Dockerfile

Modify the API image to:

  • Run as a non-root user.
  • Use a .dockerignore.
  • Avoid pip cache files.
  • Separate stable dependency installation from source-code copying.
  • Add a health check.

Example:

HEALTHCHECK --interval=30s --timeout=3s \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')"
Enter fullscreen mode Exit fullscreen mode

Test the result:

docker inspect --format='{{json .State.Health}}' container_name
Enter fullscreen mode Exit fullscreen mode

17. Quick Interview Revision

Here are concise answers you can practice aloud:

  1. What is Docker?

    A platform for packaging and running applications in isolated, portable containers.

  2. Image versus container?

    An image is an immutable template; a container is a running instance of that image.

  3. What does -p 8080:5000 mean?

    It maps host port 8080 to container port 5000.

  4. Does EXPOSE publish a port?

    No. It documents the intended container port. Port publishing requires -p or Compose’s ports.

  5. Why use .dockerignore?

    To reduce build context size and prevent unnecessary or sensitive files from entering the build.

  6. Why use multi-stage builds?

    To keep build tools and temporary files out of the final runtime image.

  7. What happens to container data after removal?

    Data in the container’s writable layer is lost; data in a named volume can persist.

  8. How do containers communicate in Compose?

    Through the Compose network, usually using service names as DNS hostnames.

  9. Why should applications not depend on localhost for another service?

    Because localhost refers to the current container.

  10. How do you troubleshoot a stopped container?

    Check docker ps -a, inspect its exit status, and read docker logs.

  11. What is a good Docker security practice?

    Use trusted, versioned images, avoid secrets in images, run as non-root, limit privileges, and scan regularly.

  12. How do you improve build speed?

    Use .dockerignore, order layers for caching, avoid unnecessary context files, and use multi-stage or BuildKit caching where appropriate.

Learning Resources

For continued practice, work through Docker’s official getting-started material and inspect its sample applications rather than only reading command references. The official examples demonstrate containers, images, volumes, bind mounts, networking, Compose, caching, and multi-stage builds in a connected workflow. github
Developers preparing for an AWS DevOps course and placement in Bangalore can use this hands-on Docker lab to practice containerization, networking, image optimization, Docker Compose, and troubleshooting.

The most effective routine is:

  1. Build a small service.
  2. Containerize it.
  3. Add a second service.
  4. Persist its data.
  5. Break the networking deliberately.
  6. Diagnose the failure from logs and inspection commands.
  7. Improve the Dockerfile.
  8. Automate the build with CI.

That process prepares you for Docker interviews much better than memorizing commands alone.

Top comments (0)