Top 67 Docker Interview Questions & Answers (2026)
About Docker
Top 100 Docker interview questions covering containers, images, Dockerfile, Docker Compose, networking, volumes, orchestration, and production best practices. Companies hiring for Docker roles test this knowledge at every stage — from first-round screens that check the basics to final-round conversations about real-world trade-offs.
What to Expect in a Docker Interview
Expect a mix of conceptual and practical Docker questions: clear definitions and core-concept checks for junior roles, hands-on scenario questions for mid-level roles, and architecture or trade-off discussions for senior roles. Interviewers usually move from foundational topics toward the kind of problems you'd actually face on the job.
How to Use This Guide
Work through the Docker questions in order — Beginner, then Intermediate, then Advanced — so each concept builds on the last. Every question has its own page; bookmark the ones that trip you up and revisit them the day before your interview.
Curated by Tech Baithak Editorial Team · Last updated: May 2026
Core concepts every Docker developer must know.
01
What is Docker?
Docker is an open-source platform that automates the deployment, scaling, and management of applications using containerization technology. It was released in 2013 by Docker, Inc. and quickly became the industry standard for packaging and running applications in lightweight, isolated environments called containers. Docker enables developers to package an application together with all its dependencies — code, runtime, system libraries, environment variables, and configuration — into a single portable unit. This container can then run consistently on any machine that has Docker installed, regardless of the underlying operating system or infrastructure. The famous promise: "works on my machine" becomes "works on every machine." Docker is built on Linux kernel features: namespaces (process isolation), cgroups (resource limiting), and union file systems (layered image storage). Docker is used extensively in development, CI/CD pipelines, and production deployments, and forms the foundation for container orchestration with Kubernetes.
02
What is a container?
A container is a lightweight, standalone, executable package that includes everything needed to run an application: code, runtime, system tools, libraries, and settings. Containers are instances of Docker images running as processes. Key characteristics: (1) Isolation: containers have their own filesystem, process space, network interfaces, and users — isolated from each other and the host, using Linux namespaces; (2) Lightweight: containers share the host OS kernel (unlike VMs which have their own OS kernel) — they start in seconds and use MB of memory instead of GB; (3) Portable: a container runs the same way on a developer's laptop, CI server, and production cloud; (4) Ephemeral: containers are designed to be stateless and disposable — stop, delete, and recreate without data loss (data lives in volumes); (5) Immutable: the container's filesystem (from the image) is read-only — any writes go to a writable layer. Containers vs. Virtual Machines: both provide isolation, but VMs virtualize hardware and run a full OS kernel (hundreds of MB to GB overhead, minutes to start); containers share the host kernel and are much more lightweight (MB, seconds to start).
03
What is a Docker image?
A Docker image is a read-only template used to create containers. It is a layered, immutable snapshot of a filesystem with all the dependencies, libraries, code, and settings needed to run an application. Images are built from a Dockerfile — a text file with instructions. Each instruction in the Dockerfile creates a new read-only layer in the image. Layers are cached and reused — if a layer hasn't changed, Docker uses the cached version, making builds fast. When you run an image, Docker adds a thin writable container layer on top. Image naming: repository:tag — e.g., nginx:1.25, node:20-alpine, ubuntu:22.04. Without a tag, :latest is assumed (but relying on latest in production is risky — pin specific versions). Images can be stored in registries: Docker Hub (public, default), Amazon ECR, Google GCR, GitHub Container Registry, or self-hosted. Commands: docker pull nginx:1.25 — download an image; docker images — list local images; docker rmi nginx:1.25 — remove an image; docker build -t myapp:1.0 . — build from Dockerfile.
04
What is the difference between a container and a virtual machine?
Both containers and virtual machines (VMs) provide isolation, but they work at different levels of the stack: Virtual Machine: runs on a hypervisor (VMware, VirtualBox, KVM) which virtualizes physical hardware. Each VM has its own full OS kernel, device drivers, and OS files. Resource overhead: typically 1-4GB RAM per VM, minutes to boot, GB of storage per image. Container: runs on the host OS kernel directly. Containers use Linux kernel features (namespaces, cgroups) to isolate processes without a separate kernel. They share the host kernel. Resource overhead: typically 10-100MB RAM per container, seconds to start, MB of storage per image layer. Isolation level: VMs provide stronger isolation (separate kernels, full OS boundary); containers provide process-level isolation — better isolation than bare processes but weaker than VMs. A kernel vulnerability affects all containers on the host. Practical comparison: 10 VMs on a 16GB server might use 10GB just for OS overhead; 100 containers on the same server might use 2GB for base OS layers. Use VMs when you need strong security isolation (multi-tenant), different OS types (Linux + Windows), or legacy applications. Use containers for microservices, modern cloud-native apps, and CI/CD.
05
What is a Dockerfile?
A Dockerfile is a plain text file (named exactly "Dockerfile", no extension) containing a series of instructions that Docker executes to build an image. Each instruction creates a new layer in the image. Key instructions: FROM — specifies the base image: FROM node:20-alpine; RUN — executes a command during build: RUN npm install; COPY — copies files from host into the image: COPY . /app; ADD — like COPY but also supports URLs and auto-extracts archives (prefer COPY for clarity); WORKDIR — sets the working directory for subsequent instructions: WORKDIR /app; EXPOSE — documents which port the container listens on (informational, doesn't actually publish): EXPOSE 3000; ENV — sets environment variables: ENV NODE_ENV=production; ARG — build-time variable (not persisted in image); CMD — default command to run when container starts (overridable): CMD ["node", "app.js"]; ENTRYPOINT — sets the main process (CMD arguments are appended): ENTRYPOINT ["node"]; VOLUME — declares a mount point; USER — sets the user to run subsequent commands; LABEL — adds metadata.
06
What is Docker Hub?
Docker Hub is Docker's official cloud-based registry service — the world's largest library of container images. It is the default registry Docker uses when you docker pull an image without specifying a registry. Docker Hub hosts: (1) Official images: curated, security-scanned images for popular software maintained by Docker or the software vendor — nginx, mysql, node, python, ubuntu. Use these as base images; (2) Verified publisher images: images from certified companies (AWS, Oracle, etc.); (3) Community images: images published by individuals and organizations. Image naming convention: official images have no prefix (nginx:1.25); user/organization images have a username prefix (username/myapp:latest). Limits: Docker Hub free tier has pull rate limits (100 pulls/6 hours anonymously, 200 authenticated). For production or CI, use a private registry (Amazon ECR, Google GCR, GitHub Container Registry, self-hosted Harbor) or pay for Docker Hub Pro. Commands: docker login — authenticate; docker push username/myapp:1.0 — push your image; docker search nginx — search for images.
07
What is the docker run command?
docker run creates and starts a new container from an image. It is the most commonly used Docker command, combining docker create and docker start. Syntax: docker run [options] image [command] [args]. Essential options: -d / --detach — run in the background (daemon mode), print container ID; -p 8080:3000 / --publish — map host port 8080 to container port 3000; -e NODE_ENV=production / --env — set environment variable; --name myapp — assign a name (instead of random name); -v /host/path:/container/path / --volume — mount a volume; --rm — automatically remove the container when it exits (great for one-off tasks); -it — interactive terminal (-i keeps stdin open, -t allocates a pseudo-TTY); --network — connect to a specific network; --restart unless-stopped — restart policy; --memory 512m — memory limit; --cpus 1.5 — CPU limit. Example: docker run -d -p 80:3000 -e NODE_ENV=production --name myapi --restart unless-stopped myapp:1.0.
08
What is the difference between CMD and ENTRYPOINT in Dockerfile?
Both define what runs when a container starts, but they work differently: CMD provides the default command and arguments to run. It can be completely overridden by providing a command after the image name in docker run: docker run myimage /bin/bash overrides CMD. Three forms: shell form: CMD npm start; exec form (preferred): CMD ["node", "app.js"]; list form (args for ENTRYPOINT). ENTRYPOINT sets the main executable — it is NOT overridden by docker run command arguments; those arguments are appended to ENTRYPOINT instead. Override ENTRYPOINT only with docker run --entrypoint. Two forms: shell form: ENTRYPOINT npm (runs in shell, signals not forwarded properly); exec form (preferred): ENTRYPOINT ["node"]. Together: use ENTRYPOINT for the main executable and CMD for default arguments: ENTRYPOINT ["node"]; CMD ["app.js"] — runs node app.js by default; docker run myimage server.js runs node server.js (CMD overridden but ENTRYPOINT preserved). Best practices: use exec form (JSON array syntax) for both to ensure signals (SIGTERM for graceful shutdown) are sent directly to the process, not to a shell wrapper.
09
What is a Docker volume?
A Docker volume is a mechanism for persisting data generated by and used by containers. Containers are ephemeral — when a container is removed, its writable layer (and any data written to the container filesystem) is lost. Volumes solve this by storing data outside the container's lifecycle. Three types of data mounting: (1) Named volumes: Docker manages the storage location on the host (docker run -v mydata:/var/lib/mysql mysql). Best for production — Docker handles the path; can be shared between containers; persists after container removal; managed with docker volume ls, docker volume create, docker volume rm; (2) Bind mounts: mount a specific host path into the container (docker run -v /home/user/data:/app/data myapp). Host and container share the directory. Best for development (live code reloading — mount source code); (3) tmpfs mounts: stored in host memory only, never written to disk — for sensitive temporary data. Use volumes for: database files, user uploads, application logs, SSL certificates. Commands: docker volume ls, docker volume inspect myvolume, docker volume prune (remove unused volumes).
10
What is Docker Compose?
Docker Compose is a tool for defining and running multi-container Docker applications using a YAML configuration file (docker-compose.yml or compose.yml). Instead of running long docker run commands for each service, Compose lets you define all services, their configurations, networks, and volumes in one file, then start everything with a single command. Basic docker-compose.yml: version: "3.9"\nservices:\n web:\n build: .\n ports:\n - "3000:3000"\n environment:\n - NODE_ENV=development\n volumes:\n - .:/app\n depends_on:\n - db\n db:\n image: mysql:8.0\n volumes:\n - dbdata:/var/lib/mysql\n environment:\n MYSQL_ROOT_PASSWORD: secret\nvolumes:\n dbdata:. Key commands: docker compose up -d — start all services in background; docker compose down — stop and remove containers; docker compose logs -f web — follow logs; docker compose exec web bash — open shell in running service; docker compose build — rebuild images; docker compose ps — list service status. Services on the same Compose network can reach each other by service name (db, web).
11
What are Docker networking modes?
Docker provides several networking modes that control how containers communicate: (1) bridge (default for standalone containers): Docker creates a virtual bridge network. Each container gets an IP on this private network; they communicate via IPs or container names (with DNS on user-defined networks). The host connects via port mapping (-p 8080:3000). Use user-defined bridge networks instead of the default bridge — they provide automatic DNS resolution by container name; (2) host: the container shares the host's network namespace — no isolation. Container binds directly to host ports (no port mapping). Best performance, worst isolation. Linux only (not available on Mac/Windows Docker Desktop); (3) none: no network — completely isolated. Useful for containers that don't need network access (batch processing); (4) overlay: for Docker Swarm — spans multiple Docker hosts, enabling containers on different machines to communicate as if on the same network; (5) macvlan: assigns a real MAC address to the container, making it appear as a physical device on the network. Useful for legacy applications expecting direct network access. Commands: docker network create mynet, docker network ls, docker network connect mynet container.
12
What is the difference between ADD and COPY in Dockerfile?
Both ADD and COPY copy files from the build context (your local machine) into the Docker image. COPY is the simpler, preferred choice for most use cases: COPY source destination — copies files/directories from the build context into the image. It is explicit and predictable — no hidden behaviors. Examples: COPY package.json /app/; COPY src/ /app/src/. ADD has two additional capabilities beyond COPY: (1) URL support: ADD https://example.com/file.tar.gz /tmp/ — downloads from a URL (but this creates a layer without caching benefits; use curl/wget in RUN instead for better control); (2) Auto-extraction: if the source is a local tar archive (gz, bz2, xz), ADD automatically extracts it into the destination directory — ADD myapp.tar.gz /app/ extracts the contents. Best practices: use COPY by default — its behavior is transparent and predictable. Use ADD only when you specifically need its auto-extraction feature. The Docker documentation explicitly recommends preferring COPY over ADD for clarity.
13
What is a Docker registry?
A Docker registry is a storage and distribution system for Docker images. It allows you to push (upload), pull (download), and share Docker images across teams and environments. Docker Hub is the most well-known public registry. Types: Public registries: Docker Hub (docker.io), GitHub Container Registry (ghcr.io), Quay.io — images publicly accessible. Private registries: Amazon ECR (Elastic Container Registry), Google GCR/Artifact Registry, Azure Container Registry, GitHub Container Registry (private repos) — require authentication. Self-hosted: Docker Registry (open-source), Harbor (enterprise-grade, with scanning and role-based access), JFrog Artifactory. Registry interaction: docker login ghcr.io — authenticate; docker tag myapp:1.0 ghcr.io/username/myapp:1.0 — tag with full registry path; docker push ghcr.io/username/myapp:1.0 — push; docker pull ghcr.io/username/myapp:1.0 — pull. In Kubernetes, configure registry credentials as Kubernetes secrets for pulling from private registries. For CI/CD, use ephemeral registry credentials via environment variables or service accounts. Always use specific image tags (not latest) in production for reproducibility.
14
What is the purpose of .dockerignore?
The .dockerignore file specifies files and directories that should be excluded from the build context — the set of files sent to the Docker daemon when building an image. Similar to .gitignore syntax: list patterns of files/directories to exclude. Why it matters: when you run docker build ., Docker sends ALL files in the current directory (and subdirectories) to the Docker daemon as the build context. If your project has large directories (node_modules, .git, test data, logs, build artifacts), they all get sent over even if they're not needed in the image — this makes builds slow and the image larger. Typical .dockerignore contents: node_modules/ (installed from scratch in the image via npm install), .git/, *.log, *.md (if not needed at runtime), .env (sensitive — never copy into image), coverage/, dist/ (if built fresh), docker-compose*.yml, Dockerfile*, .dockerignore. Benefits: faster builds (smaller context to transfer), smaller images (only copy what's needed), security (secrets in .env don't accidentally end up in the image), better cache utilization.
15
What is docker-compose.yml structure?
The docker-compose.yml (or compose.yml) is a YAML file defining multi-container applications. Main top-level keys: version: Compose file format version (still valid but deprecated in Compose V2 which auto-detects). services: defines each container/service. Each service can have: image (use existing image), build (build from Dockerfile — can be a path or object with context/dockerfile), ports (host:container port mapping), environment (env vars as list or map), env_file (.env file), volumes (volume mounts), depends_on (start order), networks (network membership), restart (restart policy), command (override CMD), entrypoint, healthcheck, deploy (resource limits in Swarm). volumes: declares named volumes used by services. networks: defines custom networks (Compose auto-creates one per project). configs and secrets: Docker Swarm features for configuration and secrets management. Example health check: healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s. Compose files support YAML anchors for reusing configuration blocks.
16
What are Docker image layers?
Docker images are built from a series of read-only layers, where each layer represents a set of filesystem changes created by a Dockerfile instruction (RUN, COPY, ADD). Layers are stacked on top of each other using a union filesystem (overlay2 is the default storage driver on modern Linux). Each layer stores only the changes (diff) from the previous layer — only files added, modified, or deleted. When you run a container, Docker adds a thin writable layer (container layer) on top — all writes go there; the image layers remain read-only. Layer caching: Docker caches each layer by its instruction + content. When rebuilding, if an instruction and its dependencies haven't changed, Docker reuses the cached layer — making rebuilds fast. Cache is invalidated from the changed instruction downward. Implication for Dockerfile ordering: put instructions that change frequently (COPY source code, ENV) at the end; put instructions that change rarely (installing dependencies) early. Example: COPY package.json first → npm install → COPY rest of source code. This way, npm install is cached if only source changed. Viewing layers: docker image history myapp:1.0 or docker inspect myapp:1.0. Sharing layers: if two images share layers (e.g., same base image), Docker stores the shared layers only once on disk.
17
What is the difference between docker stop and docker kill?
Both stop a running container but they do it differently: docker stop: sends a SIGTERM signal to the main process inside the container, giving it a chance to shut down gracefully — flush pending writes, close database connections, finish in-flight requests. After a timeout period (default 10 seconds, configurable with -t), if the process hasn't stopped, Docker sends SIGKILL to force-stop it. This is the recommended way to stop containers. Custom timeout: docker stop -t 30 mycontainer. docker kill: sends SIGKILL immediately (or any specified signal) — terminates the process instantly without waiting for graceful shutdown. Use with a custom signal: docker kill --signal SIGHUP mycontainer — useful for sending reload signals to nginx, etc. When to use each: use docker stop (default) for normal operations — allows the application to handle the signal for graceful shutdown; use docker kill only when a container is frozen or unresponsive to SIGTERM. For proper graceful shutdown, your application must handle SIGTERM — use exec form in CMD/ENTRYPOINT so signals are sent directly to your app process, not to a shell wrapper.
18
What is docker exec?
docker exec runs a command inside an already-running container. Unlike docker run which creates a new container, docker exec executes within the existing container's environment. Syntax: docker exec [options] container command [args]. Common uses: (1) Interactive shell: docker exec -it mycontainer bash — opens a bash shell; use sh if bash isn't available (Alpine images); (2) Run a one-off command: docker exec mycontainer php artisan migrate; docker exec mydb mysql -u root -p -e "SHOW DATABASES;"; (3) Check a file: docker exec mycontainer cat /app/config.json. Options: -i — keep stdin open (interactive); -t — allocate TTY; -e VAR=value — set env var for this exec; -u user — run as specific user; -w /path — set working directory. Important: docker exec only works on running containers. The command runs in the container's namespace and sees the container's filesystem, environment, and processes. Changes to the container's filesystem are visible to the main container process. This is the primary debugging tool for investigating container issues.
19
What is a Docker network?
A Docker network provides connectivity between containers and controls how they communicate with each other and with the outside world. Docker automatically creates three default networks: bridge — the default for standalone containers; host — uses the host's network directly; none — no networking. User-defined bridge networks are recommended over the default bridge: they provide automatic DNS resolution (containers can find each other by service name/container name, not just IP), better isolation, and the ability to dynamically add/remove containers. Create: docker network create mynet. Connect at run time: docker run --network mynet myapp. Connect running container: docker network connect mynet mycontainer. With Docker Compose, all services in the same Compose project automatically share a user-defined network and can reach each other by service name. Container DNS: in a user-defined network, ping db resolves to the db container's IP. Inspect network: docker network inspect mynet. Port exposure: EXPOSE in Dockerfile documents the port; -p 8080:3000 in docker run actually publishes it to the host (bind to 0.0.0.0:8080 by default — bind to 127.0.0.1:8080 to restrict to localhost).
20
What is docker-compose up vs docker-compose start?
docker compose up creates and starts all services defined in docker-compose.yml. If containers don't exist, it creates them; if images need to be built (services with build), it builds them; if containers already exist and haven't changed, it starts them. It also creates any defined networks and volumes. Options: -d runs in detached (background) mode; --build forces rebuild of images; --force-recreate recreates containers even if config/image haven't changed. This is the go-to command for starting your application. docker compose start starts existing stopped containers — it does NOT create containers or networks; those must already exist. If you ran docker compose up, then docker compose stop, you can use docker compose start to restart without recreation. Practical workflow: first run or after changes → docker compose up --build -d; resume after stop → docker compose start; restart everything fresh → docker compose down && docker compose up -d. docker compose down stops and removes containers and networks (but not volumes by default — add -v to also remove volumes).
21
What is the WORKDIR instruction in Dockerfile?
WORKDIR sets the working directory for all subsequent Dockerfile instructions (RUN, CMD, ENTRYPOINT, COPY, ADD) and for the container's default shell when you docker exec -it container bash. If the directory doesn't exist, WORKDIR creates it automatically. Usage: WORKDIR /app. All following instructions execute relative to this path. Best practices: always set WORKDIR explicitly rather than relying on the root directory or using cd in RUN commands. Prefer absolute paths (starting with /) for clarity. WORKDIR can be set multiple times in a Dockerfile — each new WORKDIR is relative to the previous if a relative path is given. Example Dockerfile structure: FROM node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci\nCOPY . .\nEXPOSE 3000\nCMD ["node", "app.js"]. All COPY instructions use /app as the base — COPY package*.json ./ copies to /app/package*.json. The CMD runs from /app. Why not use RUN mkdir /app && cd /app? Because cd in RUN only affects that RUN layer; WORKDIR persists across all subsequent layers.
22
What is docker build and how does it work?
docker build reads the Dockerfile and builds a Docker image from it. Syntax: docker build [options] path. The path (typically . for the current directory) defines the build context — the set of files sent to the Docker daemon. Process: (1) Docker sends the build context to the daemon (respecting .dockerignore); (2) The daemon reads the Dockerfile instructions in order; (3) Each instruction creates a new layer; (4) Layers are cached — if an instruction and context hash match a cached layer, it's reused (cache hit). Key options: -t myapp:1.0 / --tag — name and tag the resulting image; -f Dockerfile.prod / --file — specify a different Dockerfile; --no-cache — ignore all cached layers (build fresh); --build-arg KEY=value — pass ARG values; --target stage_name — build only up to a specific stage (multi-stage builds); --platform linux/amd64 — build for a specific architecture; --pull — always pull the latest base image. Build context tip: if the build context is large, specify it precisely: docker build -f docker/Dockerfile . or docker build --no-context with STDIN for small projects. Use docker buildx for multi-platform builds.
23
What is a multi-stage Dockerfile?
Multi-stage builds use multiple FROM instructions in a single Dockerfile. Each FROM starts a new build stage with its own base image. You can selectively copy artifacts from previous stages — leaving behind build tools, source code, and other unnecessary files. This produces much smaller, cleaner final images. Classic example — Node.js application: FROM node:20 AS builder\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci\nCOPY . .\nRUN npm run build\n\nFROM node:20-alpine AS production\nWORKDIR /app\nCOPY --from=builder /app/dist ./dist\nCOPY --from=builder /app/node_modules ./node_modules\nCOPY package.json .\nEXPOSE 3000\nUSER node\nCMD ["node", "dist/app.js"]. The builder stage compiles/builds the app; the production stage starts fresh from a minimal Alpine image and copies only the built output — no TypeScript compiler, dev dependencies, or source files. Result: final image might be 100MB instead of 800MB. Benefits: smaller attack surface (fewer packages = fewer vulnerabilities), faster image pushes/pulls, less storage. Build only a specific stage: docker build --target builder -t myapp:builder .. Copy from external image: COPY --from=nginx:1.25 /etc/nginx/nginx.conf /etc/nginx/nginx.conf.
24
What are environment variables in Docker?
Environment variables in Docker configure containers without hardcoding values in images. Multiple ways to set them: (1) Dockerfile ENV instruction: ENV NODE_ENV=production PORT=3000 — baked into the image, available in all containers created from it. Can be overridden at runtime; (2) docker run -e: docker run -e NODE_ENV=production -e DB_URL=mysql://... — set at container creation; (3) docker run --env-file: docker run --env-file .env myapp — read from a file; (4) Docker Compose environment: in compose.yml: environment: - NODE_ENV=production - DB_PASSWORD=${DB_PASSWORD} — supports variable interpolation from the host shell; (5) Docker Compose env_file: env_file: - .env — reads from .env file. ARG vs ENV: ARG is available only during build (--build-arg), not in the running container. ENV is available both during build and at runtime. Security: never put secrets in Dockerfile ENV (they become part of the image layers and are visible with docker inspect or docker history). Use Docker secrets (Swarm), Kubernetes secrets, or a secrets manager (Vault, AWS SSM) for sensitive values. Inspect env vars in a running container: docker exec mycontainer env or docker inspect mycontainer.
25
How do you list and manage Docker containers?
Essential Docker container management commands: Listing: docker ps — list running containers (shows ID, image, command, created, status, ports, name); docker ps -a / --all — list all containers including stopped ones; docker ps -q — only container IDs (useful for scripting). Logs: docker logs mycontainer — view container logs; docker logs -f mycontainer — follow logs (tail); docker logs --tail 100 mycontainer — last 100 lines; docker logs --since 2h mycontainer — logs from last 2 hours. Inspect: docker inspect mycontainer — full JSON configuration including IPs, volumes, env vars, labels. Resource usage: docker stats — live CPU, memory, network, disk I/O for all running containers; docker stats mycontainer — for specific container. Removing: docker rm mycontainer — remove a stopped container; docker rm -f mycontainer — force remove running container; docker container prune — remove all stopped containers. Restart: docker restart mycontainer. Rename: docker rename old_name new_name. Copy files: docker cp mycontainer:/app/log.txt ./log.txt — copy from container to host.
26
What are restart policies in Docker?
Docker restart policies control whether and how Docker automatically restarts a container when it exits. Set with --restart flag in docker run. Options: no (default): never automatically restart. Container stays stopped when it exits; on-failure[:max-retries]: restart only if the container exits with a non-zero exit code (indicating an error). Optionally limit retries: --restart on-failure:5 — retry at most 5 times with exponential backoff between retries; always: always restart the container regardless of exit code. Also restarts when the Docker daemon starts (after host reboot) — even if you manually stopped it; unless-stopped: like always but does NOT restart if you manually stopped the container with docker stop. Recommended for most production services — auto-starts after host reboot, but respects your manual stops. In Docker Compose: restart: unless-stopped. Inspect current policy: docker inspect --format "{{.HostConfig.RestartPolicy.Name}}" mycontainer. Change restart policy on running container: docker update --restart unless-stopped mycontainer. In Kubernetes, restart behavior is handled by the Pod restart policy and controllers (Deployment, DaemonSet) rather than Docker-level settings.
27
What is Docker Swarm?
Docker Swarm is Docker's native clustering and orchestration solution, built into the Docker engine. It transforms a group of Docker hosts into a single virtual Docker host, enabling you to deploy and manage multi-container applications across multiple machines. Key concepts: (1) Swarm: the cluster of Docker hosts; (2) Node: a Docker host in the swarm — either a Manager (manages cluster state, schedules tasks) or Worker (executes containers); (3) Service: the definition of tasks to execute on Swarm — like a docker-compose.yml but for clusters. Includes the image, replicas count, update config, resource limits; (4) Task: a running container that is part of a service; (5) Stack: a group of services sharing a network, deployed with a Compose file (docker stack deploy -c compose.yml mystack). Features: load balancing (built-in mesh routing), service discovery, rolling updates with rollback, scaling (docker service scale web=5), secrets management, health checks, and self-healing (restarts failed containers). Initialize: docker swarm init. Add worker: docker swarm join --token TOKEN manager-ip:2377. Docker Swarm is simpler than Kubernetes but has less ecosystem support. Most new deployments use Kubernetes instead.
28
What is the difference between EXPOSE and port publishing in Docker?
EXPOSE in Dockerfile is a documentation instruction — it declares which ports the container's application listens on, but it does NOT actually make those ports accessible from outside the container. Example: EXPOSE 3000 tells Docker and humans "this container listens on port 3000." It does not affect networking. Port publishing (-p in docker run or ports: in Compose) actually maps a container port to a host port, making it accessible from outside. Two forms: (1) -p 8080:3000 — maps host port 8080 to container port 3000 (binds to all host interfaces: 0.0.0.0); (2) -p 127.0.0.1:8080:3000 — binds only to localhost (more secure — not accessible from other machines). -p 3000 (without host port) — Docker assigns a random available host port. Why EXPOSE at all? (1) Documentation for developers reading the Dockerfile; (2) Used by docker run -P (capital P) which automatically publishes all EXPOSEd ports to random host ports; (3) Some container platforms use it for service discovery. Best practice: always EXPOSE the port in Dockerfile for documentation, and use -p or ports in Compose to control actual access. For services that should only be accessed by other containers (not externally), omit port publishing — use Docker networks instead.
29
How do you reduce Docker image size?
Smaller images are faster to build, push, pull, and have a smaller attack surface. Strategies: (1) Use minimal base images: prefer alpine variants (node:20-alpine ~50MB vs node:20 ~350MB); for compiled languages, use scratch (empty) or distroless images; (2) Multi-stage builds: separate build tools from runtime — only copy the built artifacts to the final stage; (3) Combine RUN instructions: each RUN creates a layer — combine commands with && to reduce layers and clean up in the same step: RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* — the cleanup is in the same layer and actually reduces size; (4) Remove caches and temp files: for apt: rm -rf /var/lib/apt/lists/*; for npm: npm ci --only=production or npm cache clean --force; (5) Use .dockerignore: exclude node_modules, .git, tests, docs; (6) Avoid unnecessary packages: don't install debugging tools in production images; (7) Use COPY instead of ADD; (8) Pin specific versions not :latest; (9) Analyze with dive tool (visualizes layers and wasted space) or docker image history myimage.
30
What is the USER instruction in Dockerfile?
The USER instruction sets the user (and optionally group) that the container runs as for subsequent RUN, CMD, and ENTRYPOINT instructions. By default, containers run as root — a significant security risk. If a vulnerability in the app allows code execution, running as root gives full container access and potentially host access (with certain misconfigurations). Best practice: always create a non-root user and use USER to switch to it before CMD/ENTRYPOINT. Example: FROM node:20-alpine\nWORKDIR /app\n# Install deps as root\nCOPY package*.json ./\nRUN npm ci --only=production\nCOPY . .\n# Create and switch to non-root user\nRUN addgroup -S appgroup && adduser -S appuser -G appgroup\nRUN chown -R appuser:appgroup /app\nUSER appuser\nEXPOSE 3000\nCMD ["node", "app.js"]. The node Docker image comes with a pre-created node user — you can simply use USER node. Syntax: USER username or USER username:group or use numeric IDs: USER 1000:1000 (preferred in Kubernetes for security contexts). Some operations (binding to ports below 1024) require root — use cap_add or run on a port ≥ 1024 instead.
31
What is the HEALTHCHECK instruction in Dockerfile?
The HEALTHCHECK instruction tells Docker how to test whether the container is still working correctly. Docker runs the health check command periodically and updates the container's health status. Container health states: starting (initial period), healthy (check is passing), unhealthy (check has failed consecutively). Syntax: HEALTHCHECK [options] CMD command. Options: --interval=30s — how often to run (default 30s); --timeout=10s — how long to wait for response (default 30s); --retries=3 — number of consecutive failures to consider unhealthy (default 3); --start-period=40s — grace period before checking (for slow-starting apps). Example for a web server: HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=60s CMD curl -f http://localhost:3000/health || exit 1. Use exit 0 for healthy, exit 1 for unhealthy. View health status: docker ps shows health status; docker inspect --format "{{.State.Health.Status}}" container. In Docker Compose, depends_on can wait for a service to be healthy: depends_on: db: condition: service_healthy. In Docker Swarm, unhealthy tasks are automatically replaced. In Kubernetes, use liveness and readiness probes (separate, more powerful) instead.
32
What is docker inspect?
docker inspect returns detailed low-level information about Docker objects (containers, images, networks, volumes) in JSON format. It is invaluable for debugging. For containers: docker inspect mycontainer — returns a large JSON object including: (1) container ID, name, state (running, exit code, started/finished time); (2) network configuration (IP addresses, ports, gateway); (3) mounted volumes and their host paths; (4) environment variables; (5) resource limits (CPU, memory); (6) restart policy; (7) labels. Extract specific fields with --format using Go template syntax: docker inspect --format "{{.NetworkSettings.IPAddress}}" mycontainer — get container IP; docker inspect --format "{{.State.ExitCode}}" mycontainer — get exit code; docker inspect --format "{{json .Config.Env}}" mycontainer — get env vars as JSON. For images: docker inspect nginx:1.25 — shows layers, config, exposed ports, architecture. For networks: docker inspect mynetwork — shows connected containers and their IPs. For volumes: docker inspect myvolume — shows mount point on host. Use docker inspect when debugging: container can't connect to another service (check IPs and networks), env vars not set correctly, volume not mounted at expected path.
33
What is docker pull and docker push?
docker pull downloads a Docker image from a registry to your local machine. Syntax: docker pull image[:tag]. Examples: docker pull nginx:1.25 — pull specific version (always pin versions!); docker pull node:20-alpine; docker pull ghcr.io/username/myapp:1.0 — from GitHub Container Registry. Docker pull only downloads layers not already cached locally — subsequent pulls of images sharing layers are fast. For multi-platform: docker pull --platform linux/amd64 node:20. docker push uploads a local image to a registry. You must be authenticated and the image must be tagged with the full registry path. Steps: (1) Login: docker login (Docker Hub) or docker login ghcr.io -u username --password-stdin; (2) Tag: docker tag myapp:1.0 ghcr.io/username/myapp:1.0; (3) Push: docker push ghcr.io/username/myapp:1.0. Docker pushes only layers not already in the registry. Multiple tags: you can push the same image with different tags: docker tag myapp:1.0 myapp:latest && docker push myapp:latest. Efficient workflow in CI/CD: build → tag → push to registry → trigger deployment (pull from registry to target servers or Kubernetes).
34
What is the difference between image tags and digests?
Image tags are human-readable labels attached to a specific image version, like nginx:1.25 or myapp:latest. Tags are mutable — the same tag can be reassigned to a different image ID over time. This is why relying on :latest in production is dangerous — the image it points to can change without notice. Image digests are immutable, cryptographic SHA256 hashes of the image content: nginx@sha256:abc123.... Using a digest guarantees you always get the exact same image bytes, regardless of what the tag points to. Find a digest: docker images --digests nginx or docker inspect --format "{{.RepoDigests}}" nginx:1.25. Pull by digest: docker pull nginx@sha256:abc123.... In production Kubernetes manifests, using digests is a security best practice: image: nginx@sha256:abc123... — prevents tag mutable hijacking (tag confusion attacks where an attacker pushes a malicious image with the same tag). CI/CD best practice: build image → get digest from push output → deploy using digest. Tools like crane and skopeo help manage image digests. Content trust (DOCKER_CONTENT_TRUST=1) enables Notary-based image signing for verifying image authenticity.
35
What is docker system prune?
docker system prune removes all unused Docker resources to reclaim disk space. By default, it removes: (1) All stopped containers; (2) All networks not used by at least one container; (3) All dangling images (images with no tags, left over from builds); (4) All build cache. It does NOT remove unused volumes by default (too risky — could delete data). Add -a / --all to also remove unused images (not just dangling): docker system prune -a — more aggressive, reclaims more space. Add --volumes to also remove unused volumes: docker system prune -a --volumes — dangerous in production! Prompts for confirmation; use -f / --force to skip prompt. Specific prune commands: docker container prune — stopped containers; docker image prune — dangling images; docker image prune -a — all unused images; docker network prune — unused networks; docker volume prune — unused volumes; docker builder prune — build cache. Check disk usage first: docker system df — shows how much space is used by images, containers, volumes, and build cache. Schedule regular cleanup in CI environments or on long-running build servers to prevent disk exhaustion.
Practical knowledge for developers with hands-on experience.
01
What is Docker BuildKit and why is it better?
BuildKit is the next-generation build backend for Docker, enabled by default in Docker Desktop and Docker Engine 23.0+. It provides significant improvements over the legacy builder: (1) Parallel building: BuildKit analyzes stage dependencies and builds independent stages in parallel; (2) Better cache: cache is more granular and can be exported/imported across machines (inline cache in registry, external cache with --cache-from/--cache-to); (3) Secrets at build time: RUN --mount=type=secret,id=npmrc cat /run/secrets/npmrc — pass secrets without baking them into image layers; (4) SSH forwarding: RUN --mount=type=ssh git clone git@github.com:private/repo — use SSH keys without embedding them; (5) Bind mounts in RUN: RUN --mount=type=bind,source=.,target=/src; (6) Cache mounts: RUN --mount=type=cache,target=/root/.npm npm install — persistent cache between builds (huge for npm, pip, apt); (7) Faster and more informative output; (8) Multi-platform builds with docker buildx. Enable in older versions: DOCKER_BUILDKIT=1 docker build .. Use docker buildx build for full BuildKit features. BuildKit is the foundation for multi-architecture image building.
02
How do you handle secrets in Docker?
Secrets management in Docker requires careful handling to avoid embedding sensitive data in images or exposing it in environment variables (visible via docker inspect). Approaches: (1) Docker Secrets (Swarm): docker secret create db_password ./password.txt — stored encrypted in Swarm's Raft store; mounted as a file at /run/secrets/db_password inside containers; only available to services explicitly granted access: docker service create --secret db_password myapp; (2) BuildKit secrets for build time: docker buildx build --secret id=npmrc,src=~/.npmrc . and in Dockerfile: RUN --mount=type=secret,id=npmrc npm install — the secret is available only during that RUN, never in an image layer; (3) Environment variables from secrets manager: at container start, fetch secrets from HashiCorp Vault, AWS SSM Parameter Store, or AWS Secrets Manager and inject as env vars or files; (4) Kubernetes secrets: when in Kubernetes, use K8s secrets with tight RBAC or Sealed Secrets. Never: hardcode secrets in Dockerfile ENV, commit .env files with secrets, pass secrets as --build-arg (visible in docker history). Practice: rotate secrets regularly, use short-lived credentials, audit access logs.
03
What is Docker Compose override and multiple Compose files?
Docker Compose supports multiple configuration files that are merged together, enabling environment-specific configurations without duplicating the base configuration. Override files: docker-compose.override.yml is automatically merged with docker-compose.yml when you run docker compose up without specifying files. This is typically used for development-specific overrides (volume mounts for live code reloading, debug ports, additional env vars). Multiple -f files: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d — merges the files in order. Later files override earlier ones for scalar values; lists (volumes, ports, environment) are merged/appended. Common pattern: docker-compose.yml — base service definitions; docker-compose.override.yml — development overrides (automatically loaded); docker-compose.prod.yml — production overrides (explicitly specified); docker-compose.test.yml — test environment configuration. Merging rules: string values are replaced by the later file; lists are concatenated. Create a merged output: docker compose -f docker-compose.yml -f docker-compose.prod.yml config — outputs the merged YAML without running anything. Use COMPOSE_FILE env var to set default file list.
04
How does Docker handle logging?
Docker captures output from container's stdout and stderr and routes it through a logging driver. The default driver is json-file — logs are stored as JSON files on the host at /var/lib/docker/containers/<container-id>/<container-id>-json.log. Available drivers: json-file (default), syslog, journald, gelf (Graylog), fluentd, awslogs (CloudWatch), splunk, none. Configure globally in /etc/docker/daemon.json: {"log-driver": "json-file", "log-opts": {"max-size": "100m", "max-file": "3"}}. Per container: docker run --log-driver fluentd myapp. Log rotation is critical — without limits, json-file logs can fill the disk. Configure: --log-opt max-size=100m --log-opt max-file=3 (keeps last 3 files of 100MB each). Application best practice: write logs to stdout/stderr (not to files inside the container) — Docker captures and routes them. Commands: docker logs mycontainer; docker logs -f --tail 100 mycontainer. For production: use a log aggregation system — ship logs to Elasticsearch (via Fluentd/Logstash), CloudWatch, Datadog, or Splunk. docker compose logs -f combines logs from all services with color-coded prefix.
05
What is Docker context?
A Docker context stores the connection configuration needed to connect to a Docker daemon — the endpoint, authentication, and TLS certificates. Docker uses contexts to switch between different Docker environments (local, remote server, Docker Desktop, cloud) without reconfiguring environment variables. Default context: default — connects to the local Docker daemon via /var/run/docker.sock. Commands: docker context ls — list all contexts; docker context use production-server — switch active context; docker context create remote-server --docker "host=ssh://user@server.com" — create SSH-based remote context; docker context inspect contextname — inspect context details. When you switch context, all Docker commands (docker ps, docker run, docker build) execute against that environment. Use cases: (1) Manage multiple remote Docker hosts from your local machine; (2) Switch between Docker Desktop (dev) and a remote Docker host (staging); (3) Docker Desktop creates contexts for different engines (Windows/Linux containers); (4) Used by Docker Compose — DOCKER_CONTEXT env var. With buildx, contexts enable building on remote build machines. SSH context creates an SSH tunnel automatically — no need to expose the Docker daemon socket over TCP (which should be protected with TLS or kept local).
06
What is the Docker socket and why is exposing it dangerous?
The Docker socket (/var/run/docker.sock) is a Unix socket that the Docker CLI uses to communicate with the Docker daemon. The daemon listens on this socket for API commands. When you run docker ps, the CLI sends an HTTP request through this socket to the daemon. Mounting the Docker socket into a container (-v /var/run/docker.sock:/var/run/docker.sock) allows the container to control the Docker daemon directly — create containers, pull images, execute commands in other containers. This is used by: CI/CD tools (Jenkins, GitLab Runner doing Docker builds), monitoring tools (Portainer, cAdvisor), and Docker-in-Docker scenarios. Why it's dangerous: a container with Docker socket access has effectively root access to the HOST. The process inside the container can: create a privileged container mounting the host filesystem, escape container isolation entirely, read secrets from other containers, destroy all containers. This is a critical security boundary to understand. Safer alternatives: (1) Docker-in-Docker (dind): run a separate Docker daemon inside a privileged container — isolated but privileged container still risky; (2) BuildKit remote builder: use docker buildx with a remote build instance; (3) Kaniko: builds Docker images inside containers without Docker daemon access; (4) Podman: rootless container runtime.
07
What is container orchestration and what problems does it solve?
Container orchestration automates the deployment, scaling, networking, availability, and lifecycle management of containerized applications across a cluster of machines. Problems it solves that raw Docker doesn't handle: (1) Scheduling: deciding which host a container should run on based on resource availability, affinity rules, and constraints; (2) Scaling: automatically scaling services up/down based on load (horizontal pod autoscaling); (3) Self-healing: detecting and replacing failed containers automatically; restarting containers that crash; replacing containers on failed nodes; (4) Rolling updates & rollbacks: deploying new versions with zero downtime; rolling back if the new version fails health checks; (5) Service discovery & load balancing: automatically registering new instances; distributing traffic across healthy instances; (6) Configuration & secret management: distributing config and secrets to containers securely; (7) Storage orchestration: automatically mounting storage systems (AWS EBS, NFS, CSI drivers); (8) Resource management: setting CPU/memory requests and limits, bin-packing containers efficiently. Main orchestration platforms: Kubernetes (industry standard, most powerful and complex), Docker Swarm (simpler, built into Docker), Amazon ECS/EKS, HashiCorp Nomad (polyglot, simpler than K8s).
08
What is Kubernetes and how does it relate to Docker?
Kubernetes (K8s) is an open-source container orchestration platform originally developed by Google, now maintained by the CNCF (Cloud Native Computing Foundation). It automates deployment, scaling, and management of containerized applications across clusters. Relationship to Docker: Kubernetes uses containers (Docker or other OCI-compliant runtimes like containerd, CRI-O) as the building blocks. Docker packages applications into container images; Kubernetes schedules and runs those containers at scale across many nodes. Kubernetes deprecated Docker as its default container runtime (in favor of containerd) but still runs Docker images — Docker images follow the OCI (Open Container Initiative) image spec which all runtimes support. Key Kubernetes concepts: Pod — smallest deployable unit (one or more containers sharing network/storage); Deployment — declares desired state for stateless apps (replicas, update strategy); Service — stable network endpoint for a set of Pods; ConfigMap/Secret — configuration and secrets storage; Ingress — HTTP routing and TLS termination; PersistentVolume — storage; Namespace — logical isolation within a cluster. Managed K8s: EKS (AWS), GKE (Google), AKS (Azure) — they manage the control plane for you.
09
What is a Docker registry mirror?
A Docker registry mirror (also called a pull-through cache) is a local or geographically close replica of a public registry (like Docker Hub). When a Docker daemon is configured to use a mirror, it first checks the mirror for the requested image; if not cached, it fetches from the upstream registry and caches it locally for future pulls. Benefits: (1) Speed: local network pulls are much faster than pulling from Docker Hub over the internet; (2) Rate limit bypass: Docker Hub has pull rate limits (100/6h anonymous, 200/6h authenticated). A mirror serving your whole organization counts as one authenticated user; (3) Reliability: if Docker Hub is down, cached images are still available; (4) Reduced bandwidth costs. Configure in /etc/docker/daemon.json: {"registry-mirrors": ["https://your-mirror.example.com"]}. Restart Docker daemon after. Popular mirror implementations: AWS ECR Public Gallery as a mirror; self-hosted with Docker Registry (running as a pull-through cache); Nexus Repository; Harbor; JFrog Artifactory. In CI environments, mirrors dramatically reduce build times. Some cloud providers (like AWS EC2, GKE) host mirrors of Docker Hub for their users. ECR also serves as a regional cache for public images.
10
What is container security scanning?
Container security scanning analyzes Docker images for known vulnerabilities in OS packages and application dependencies (CVEs — Common Vulnerabilities and Exposures). Scanning should be part of the CI/CD pipeline — don't deploy images with critical vulnerabilities. Tools: (1) Docker Scout (built into Docker Desktop/Hub): docker scout cves myimage:1.0; (2) Trivy (Aqua Security, free, popular): trivy image myimage:1.0 — scans OS packages, npm, pip, Maven dependencies; (3) Snyk: snyk container test myimage:1.0; (4) Grype: grype myimage:1.0 — fast, open-source; (5) Amazon Inspector: continuous scanning of ECR images; (6) Clair: open-source for self-hosted registries; (7) Harbor: registry with built-in Trivy scanning. Best practices: (1) Base your images on minimal, regularly updated base images (e.g., node:20-alpine with scheduled rebuilds); (2) Pin dependency versions; (3) Scan in CI before pushing to registry; (4) Set a vulnerability threshold (block on CRITICAL, warn on HIGH); (5) Regularly rebuild and re-scan existing images; (6) Separate build-time tools from runtime (multi-stage) — fewer packages = fewer vulnerabilities; (7) Run as non-root (USER instruction).
11
What is Docker in Docker (DinD)?
Docker-in-Docker (DinD) is the technique of running the Docker daemon inside a Docker container, allowing that container to build and run Docker containers itself. Use case: CI/CD systems where build jobs run in Docker containers and need to build Docker images. Implementation: (1) Privileged DinD: run a container with Docker daemon inside: docker run --privileged docker:dind — the inner daemon needs privileged mode to create namespaces and manage cgroups; (2) Docker socket mounting (sidecar approach): mount the host Docker socket: -v /var/run/docker.sock:/var/run/docker.sock — the container uses the host's daemon directly (this is NOT truly DinD — it's using the host daemon). Problems with true DinD: security risks (privileged mode), shared cgroups leading to resource conflicts, Docker storage driver conflicts, instability. Better alternatives: (1) Kaniko: Google's tool that builds images from Dockerfiles inside Kubernetes pods without Docker daemon — most secure; (2) Buildah: builds OCI images without a daemon; (3) img: daemonless image building; (4) docker buildx with remote builder: build remotely; (5) Earthly: build tool that doesn't need DinD. Most modern CI systems (GitHub Actions, GitLab CI, CircleCI) have native Docker build support without needing DinD.
12
What is the difference between Docker and Podman?
Podman is a daemonless, rootless container tool developed by Red Hat as an alternative to Docker. Key differences: (1) Architecture: Docker requires a running daemon (dockerd) that all commands talk to. Podman is daemonless — each podman run directly creates a container process without a central daemon. No single point of failure; no daemon to manage; (2) Rootless containers: Podman can run containers without root privileges by default, using user namespaces. Docker requires root or membership in the docker group (which effectively gives root). Rootless Podman is more secure; (3) Compatibility: Podman is largely CLI-compatible with Docker — most Docker commands work with alias docker=podman; supports Docker Compose via podman-compose; (4) Pods: Podman natively supports the concept of pods (groups of containers sharing network/storage namespace) — closer to Kubernetes; (5) Systemd integration: Podman generates systemd unit files for containers; (6) Image format: both use OCI-compatible images; (7) Kubernetes: podman generate kube generates Kubernetes YAML from running pods. Podman is the default container tool in RHEL/Fedora. Docker is still dominant in general use and developer tooling. The choice often depends on your environment and team familiarity.
13
What is the principle of least privilege in Docker?
The principle of least privilege in Docker means giving containers only the capabilities and access they need — no more. Applied to Docker: (1) Non-root user: use USER in Dockerfile; set runAsNonRoot: true in Kubernetes; (2) Read-only filesystem: docker run --read-only myapp — container cannot write to its filesystem (mounts tmpfs for /tmp if needed); (3) Drop capabilities: Linux capabilities grant specific privileges. Docker grants ~14 by default. Drop all and add only needed: docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp (NET_BIND_SERVICE lets you bind to ports <1024 without root); (4) No privileged mode: never use --privileged unless absolutely necessary (gives container nearly all host capabilities); (5) No host namespaces: avoid --network host, --pid host, --ipc host unless necessary; (6) Minimal images: fewer packages = smaller attack surface; (7) seccomp profiles: restrict system calls the container can make; (8) AppArmor/SELinux: mandatory access control profiles; (9) Immutable volumes: mount volumes as read-only when container only needs to read: -v data:/app/data:ro; (10) Limited resource access: set CPU and memory limits to prevent resource exhaustion.
14
What is Docker Compose profiles?
Docker Compose profiles allow you to selectively start services, enabling different configurations for different scenarios within the same Compose file. Services can be assigned to one or more profiles; services without a profile are always started. Assign profiles in compose.yml: services:\n web:\n image: myapp\n # no profile — always starts\n debug-proxy:\n image: mitmproxy\n profiles: [debug]\n mail-dev:\n image: mailhog\n profiles: [dev, testing]\n monitoring:\n image: prometheus\n profiles: [monitoring]. Activate profiles: docker compose --profile debug up — starts web + debug-proxy; docker compose --profile dev --profile monitoring up — starts web + mail-dev + monitoring. Set via env var: COMPOSE_PROFILES=debug,monitoring docker compose up. Use cases: (1) Debug services only started when debugging; (2) Third-party mocks (email, SMS) only in dev; (3) Monitoring stack only when needed; (4) Database admin UIs (pgAdmin, phpMyAdmin) only in development; (5) Load testing tools activated on demand. Profiles allow a single compose.yml to serve multiple purposes without maintaining separate files or commenting out services.
15
What is the difference between COPY and RUN in terms of Docker layers?
Both COPY and RUN create new layers in the Docker image, but they affect caching and image size differently. COPY creates a layer containing the copied files. The cache for this layer is invalidated when the content of the copied files changes. If you COPY . /app and any file in the build context changes, all subsequent layers are rebuilt. Best practice: copy files that change rarely first, frequently-changed files last. RUN creates a layer containing the results of the command's filesystem changes. The cache is invalidated when the command string changes OR when a preceding layer's cache is invalidated. All files created and then deleted in separate RUN instructions are still in the image (in earlier layers). To actually reduce image size, combine creation and deletion in one RUN: RUN apt-get install -y curl && rm -rf /var/lib/apt/lists/* — the deletion and installation are in the same layer. Example of cache-friendly ordering: COPY package.json ./ (rarely changes) → RUN npm install (expensive, cached when package.json unchanged) → COPY src/ ./ (frequently changes, only invalidates from here). Never combine COPY . . before RUN npm install — every code change triggers reinstalling all packages.
16
What are Docker labels?
Docker labels are key-value metadata attached to Docker objects (images, containers, networks, volumes). They do not affect the container's behavior but provide useful metadata for organization, tooling, and filtering. Defining labels in Dockerfile: LABEL maintainer="team@company.com"\nLABEL version="1.0" description="MyApp API Service"\nLABEL org.opencontainers.image.source="https://github.com/org/repo"\nLABEL build.date="2024-01-15". Using OCI standard labels (org.opencontainers.image.*) is recommended for interoperability. Add labels at runtime: docker run --label env=production --label team=backend myapp. In Compose: labels: - "traefik.enable=true" - "traefik.http.routers.myapp.rule=Host(myapp.example.com)" (Traefik reverse proxy reads these to auto-configure routing). Filter by label: docker ps --filter label=env=production; docker images --filter label=version=1.0. Inspect labels: docker inspect --format "{{json .Config.Labels}}" mycontainer. Common use cases: CI/CD metadata (build number, git commit SHA, branch), Prometheus scrape configuration, reverse proxy routing (Traefik/Nginx), runbook/documentation links, team ownership, compliance/security metadata.
17
What is init in Docker containers?
Docker containers need a proper init process (PID 1) to handle zombie processes and correctly forward OS signals. Problem: when your application forks child processes (common in Node.js with worker processes, or shell scripts that spawn children), and those children exit, they become "zombie" processes if the parent doesn't collect their exit status. Normally, the Linux init process (PID 1) reaps orphaned processes. But in a container, PID 1 is your application process, which typically doesn't implement signal handling or zombie reaping. Additionally, signals (SIGTERM for graceful shutdown) sent to PID 1 may be ignored if PID 1 doesn't explicitly handle them. Solutions: (1) --init flag: docker run --init myapp — Docker injects tini (a tiny init system) as PID 1; your app becomes PID 2. Tini forwards signals and reaps zombies; (2) Include tini in Dockerfile: RUN apk add --no-cache tini\nENTRYPOINT ["/sbin/tini", "--"]\nCMD ["node", "app.js"]; (3) dumb-init: similar to tini, by Yelp; (4) Use exec form for CMD/ENTRYPOINT (JSON array) — ensures your process IS PID 1 and receives signals directly, but still doesn't handle zombie reaping; (5) For simple single-process apps without forking, exec form is sufficient.
18
What is container resource limiting in Docker?
Docker allows setting resource limits on containers to prevent any single container from consuming all host resources. Memory limits: docker run --memory 512m myapp — container cannot use more than 512MB RAM; --memory-swap 1g — total memory + swap (set equal to --memory to disable swap); without limits, one container can use all available RAM, causing OOM (Out of Memory) kills of other processes. CPU limits: docker run --cpus 1.5 myapp — container can use at most 1.5 CPU cores (out of available cores); --cpu-shares 512 — relative weight when CPUs are constrained (default 1024); --cpuset-cpus "0,1" — restrict to specific CPU cores. In Docker Compose: deploy: resources: limits: cpus: "0.5" memory: 512M reservations: cpus: "0.25" memory: 256M. Why limits matter: in Kubernetes, resource requests/limits are critical for scheduling and QoS; without limits, a buggy container (memory leak, infinite loop) degrades or crashes all co-located services. Monitoring: docker stats shows live CPU/memory usage. Observe memory usage in production and set limits ~20% above normal peak usage.
19
What is a Docker healthcheck and how does it differ from a liveness probe?
Both Docker healthchecks and Kubernetes liveness/readiness probes check container health, but they operate in different contexts with different consequences. Docker HEALTHCHECK (Dockerfile/Compose): runs a command inside the container periodically; updates the container's health status (healthy/unhealthy/starting); in Docker Swarm, unhealthy tasks are replaced; Docker Compose respects health status for depends_on with condition: service_healthy. Example: HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD curl -f http://localhost:3000/health || exit 1. Kubernetes Probes: (1) Liveness probe: determines if the container is running; if it fails, Kubernetes kills and restarts the container — for detecting deadlocks; (2) Readiness probe: determines if the container is ready to serve traffic; if it fails, Kubernetes removes the Pod from Service endpoints (stops sending traffic) but doesn't restart it — for slow startup or temporary unavailability; (3) Startup probe: protects slow-starting containers from liveness probe failures during initialization. Docker healthchecks are simpler; Kubernetes probes are more powerful and fine-grained. In Kubernetes deployments, define both liveness and readiness probes at the Pod spec level, not in the Dockerfile (though Docker healthcheck metadata can inform K8s configuration).
20
What are Docker build arguments (ARG)?
ARG defines build-time variables that can be passed to the Dockerfile during docker build with --build-arg. Unlike ENV (persists at runtime), ARG values are only available during the build process — they don't exist in the running container. Syntax in Dockerfile: ARG NODE_VERSION=20\nFROM node:${NODE_VERSION}-alpine. Pass at build time: docker build --build-arg NODE_VERSION=18 .. Default value if not provided: ARG PORT=3000. Using ARG after FROM: ARG is scoped to the stage it's defined in — if you need it across FROM stages, re-declare it after each FROM. Use cases: (1) Parameterize base image versions; (2) Pass build environment (CI build number, git SHA for labels); (3) Feature flags at build time; (4) Registry and image paths for inner images. ARG and cache: changing a build arg value invalidates the cache from that ARG onward. CRITICAL security warning: ARG values ARE visible in docker history — never use ARG for secrets (passwords, API keys). Use BuildKit secrets instead: RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret. ARG vs ENV: use ARG for build-time configuration; ENV for runtime configuration. You can convert ARG to ENV: ARG APP_VERSION\nENV APP_VERSION=${APP_VERSION}.
21
What is Docker content trust?
Docker Content Trust (DCT) is a security feature that uses digital signatures to verify the integrity and publisher of Docker images. It is built on Notary, a framework for content signing and verification. When DCT is enabled, Docker only pulls and runs images that have been signed by a trusted publisher. Enable: export DOCKER_CONTENT_TRUST=1. With DCT enabled: docker pull verifies the image signature; unsigned images are rejected; docker push automatically signs the image with your private key. Signing workflow: (1) Generate keys: DCT automatically creates root and repository keys on first push; (2) Push with signing: DOCKER_CONTENT_TRUST=1 docker push registry/image:tag; (3) Pull verifies signature against the trust data stored in a Notary server. Trust data is stored alongside the image in the registry. Keys: root key (offline, most important), repository key (for each repository), and timestamp key (freshness). Limitations: Notary V1 (original DCT) has usability issues and is being superseded by Sigstore/cosign — a keyless signing tool that uses OIDC identity (GitHub Actions, Google Workload Identity) for signing, making it much easier to integrate into CI/CD without managing private keys. cosign sign myimage:1.0 and cosign verify myimage:1.0.
Deep expertise questions for senior and lead roles.
01
What is containerd and how does it relate to Docker?
containerd is an industry-standard, high-level container runtime that manages the complete lifecycle of containers: image pull, storage, execution, and networking. It was originally part of Docker and donated to the CNCF in 2017. Docker's architecture: the Docker CLI → Docker daemon (dockerd) → containerd → runc (OCI runtime that actually creates the container using Linux namespaces/cgroups). Kubernetes deprecated Docker as its container runtime in 1.20 and removed it in 1.24, switching to using containerd (or CRI-O) directly via the CRI (Container Runtime Interface). This doesn't mean Docker images don't run in Kubernetes — they do, because Docker images follow the OCI image spec which containerd reads. runc is the OCI (Open Container Initiative) reference implementation that containerd calls to create and run containers using kernel features. crun is a newer, faster alternative to runc written in C. gVisor (runsc): Google's container runtime that provides a user-space kernel for stronger isolation (sandboxing each container with a dedicated kernel). Used in GKE Sandbox. Kata Containers: runs each container in a lightweight VM for VM-level isolation with container startup speed. Understanding this stack helps when debugging low-level container issues or configuring container runtimes in Kubernetes.
02
What are Linux namespaces and cgroups, and how do they enable containers?
Containers are not magic — they use two fundamental Linux kernel features: Namespaces isolate what a process can see. Each namespace type provides a different view: pid — process isolation (a process in a container only sees its own processes, PID 1 appears as the init); net — network isolation (each container has its own network stack, interfaces, IP); mnt — filesystem isolation (separate mount namespace — container has its own root filesystem); uts — hostname isolation (container has its own hostname and domain name); ipc — IPC isolation (separate message queues, semaphores); user — user ID isolation (container user IDs map to different host UIDs — enables rootless containers); cgroup — resource limit isolation (separate cgroup hierarchy view). cgroups (Control Groups) limit what resources a process can use: CPU (shares, quota, pinning to specific cores), memory (limit + OOM handling), block I/O (throttle disk I/O), network I/O (tc for traffic control), number of processes (pids limit). A container is fundamentally: a process with a set of namespaces (isolating what it sees) + cgroup limits (restricting what it can use) + a union filesystem (overlay2 for layered image + writable layer). Understanding this: docker run --pid=host shares the host PID namespace (breaks PID isolation), docker run --network=host shares the host network namespace.
03
What is overlay2 storage driver and how does it work?
overlay2 is Docker's default and recommended storage driver on Linux (for kernel 4.0+). It implements Docker's layered filesystem using the Linux OverlayFS (overlay filesystem) kernel feature. How it works: OverlayFS merges multiple directories (layers) into a unified view. It has three components: (1) lowerdir — read-only image layers (multiple layers stacked); (2) upperdir — writable container layer; (3) merged — the unified view the container sees. When a container reads a file: OverlayFS looks in upperdir first, then lowerdir. When a container writes/modifies a file that exists only in a read-only lowerdir layer, OverlayFS performs a copy-on-write (CoW): copies the entire file to upperdir, then modifies it there. When a container deletes a lower layer file, OverlayFS creates a "whiteout" file in upperdir to mask it. Storage on disk: /var/lib/docker/overlay2/ — each layer has its own directory. Image layers are shared on disk — multiple containers using the same image share the same lower layers; only the upperdir (container layer) is unique per container. Other storage drivers: aufs (legacy), btrfs, zfs, devicemapper (legacy). Overlay2 is recommended for most production systems.
04
What is Docker Buildx and multi-platform builds?
Docker Buildx is Docker's extended build CLI plugin that implements the BuildKit build system with additional capabilities beyond the classic docker build. Key feature: multi-platform builds — building images for multiple CPU architectures (amd64/x86_64, arm64/aarch64, armv7, etc.) from a single build command and publishing a multi-platform image manifest. Why it matters: (1) Apple Silicon Macs (arm64) need arm64 images; (2) Raspberry Pi (arm32/arm64); (3) AWS Graviton (arm64 — cheaper than x86); (4) Shipping one image tag that works everywhere. docker buildx create --name mybuilder --driver docker-container --bootstrap\ndocker buildx use mybuilder\ndocker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t myapp:1.0 --push .. This pushes a manifest list (fat manifest) — myapp:1.0 returns the correct architecture for each puller. Emulation: Buildx uses QEMU emulation to build for foreign architectures on a single host — slower than native. For speed, use --platform linux/amd64 native builders + --platform linux/arm64 on an ARM builder, then docker buildx imagetools create to combine. Builders: Buildx supports multiple drivers — docker (default), docker-container (full BuildKit in a container, supports multi-platform), kubernetes (builds on K8s pods), remote (connect to remote BuildKit).
05
What is Docker networking at a deep level (iptables, veth pairs)?
Docker networking is implemented using Linux networking primitives. When Docker creates a bridge network, it creates a Linux bridge (virtual network switch) — typically docker0 for the default bridge or br-xxx for user-defined networks. For each container, Docker creates a veth pair (virtual Ethernet pair) — a pair of connected virtual network interfaces. One end (eth0) goes into the container's network namespace; the other end (vethXXX) stays in the host namespace and is connected to the bridge. This creates a virtual cable between the container and the bridge. iptables rules implement port publishing and masquerade (NAT): (1) Docker chains: Docker adds chains to iptables (DOCKER, DOCKER-USER, DOCKER-ISOLATION) for routing and filtering; (2) DNAT: published ports (-p 8080:3000) create DNAT rules — traffic to host:8080 is translated to container-ip:3000; (3) MASQUERADE: outbound container traffic is masqueraded (SNAT) with the host IP so return traffic routes correctly. iptables -t nat -L DOCKER -n shows Docker's NAT rules. DNS: Docker runs an embedded DNS server at 127.0.0.11 inside containers on user-defined networks — resolves container and service names. Debug networking: ip addr, ip route, bridge link, nsenter -t $(docker inspect -f "{{.State.Pid}}" container) -n ip addr.
06
What is the difference between Docker Swarm services and Docker Compose services?
Despite sharing similar YAML syntax, Docker Swarm services and Docker Compose services are different concepts operating at different scales. Docker Compose services: define containers running on a single host; used for local development and single-machine deployments; managed by the Compose CLI (docker compose); containers are not replicated across hosts; restart is local only; depends_on controls startup order; typically no placement constraints. Docker Swarm services: define tasks distributed across a multi-node cluster; multiple replicas run across multiple machines; the Swarm manager schedules and balances; provide built-in load balancing (Swarm's routing mesh distributes requests across replicas on any node); rolling updates with configurable parallelism and delay; placement constraints (node.role == worker, node.labels.environment == production); resource reservations and limits for scheduling; global mode (one replica per node) or replicated mode. Swarm uses the same Compose YAML syntax but with additional fields under deploy:: deploy: replicas: 3 update_config: parallelism: 1 delay: 10s rollback_config: ... placement: constraints: [node.role == worker] resources: limits: cpus: "0.5" memory: 512M. Deploy a stack to Swarm: docker stack deploy -c docker-compose.yml mystack.
07
What is the OCI (Open Container Initiative) and why does it matter?
The Open Container Initiative (OCI) is a Linux Foundation project established in 2015 to create open industry standards around container formats and runtimes. It prevents vendor lock-in and ensures interoperability. OCI defines two key specifications: (1) OCI Image Spec: defines the format for container images — how layers are stored, the manifest format (JSON describing layers + config), and the configuration format. Any tool producing OCI images (Docker, Podman, Buildah, Kaniko, Buildpacks) produces images any OCI-compatible runtime can run; (2) OCI Runtime Spec: defines the standard for container runtimes — how to unpack an image and create a running container using Linux namespaces, cgroups, and filesystems. Implementations: runc (reference), crun, youki (Rust), kata-runtime. Why it matters: (1) Docker images run in Kubernetes (containerd) without changes — same OCI image; (2) Podman produces Docker-compatible images; (3) Kaniko, ko (for Go), Jib (for Java) build OCI images without Docker; (4) Cloud registries (ECR, GCR, GCR) accept OCI images from any tool; (5) Enables healthy competition between runtimes (Docker, Podman, Rancher Desktop, Lima). The OCI also works on the Distribution Spec — standardizing how images are pushed to and pulled from registries.
08
What is a distroless container image?
Distroless images are container images that contain only your application and its runtime dependencies — no shell (/bin/sh), no package manager (apt/apk), no general-purpose utilities. They are maintained by Google at gcr.io/distroless/. Available variants: gcr.io/distroless/base — minimal glibc-based, no shell; gcr.io/distroless/nodejs20 — Node.js runtime; gcr.io/distroless/java17; gcr.io/distroless/python3; gcr.io/distroless/static — for Go static binaries (no glibc even). Security benefits: (1) Dramatically smaller attack surface — no shell means exploits requiring shell execution fail; (2) No package manager means attackers cannot easily install tools if they gain code execution; (3) Fewer CVEs — far fewer packages to have vulnerabilities. Example multi-stage with distroless: FROM node:20 AS builder\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci\nCOPY . .\nRUN npm run build\n\nFROM gcr.io/distroless/nodejs20\nWORKDIR /app\nCOPY --from=builder /app/dist /app/dist\nCOPY --from=builder /app/node_modules /app/node_modules\nCMD ["/app/dist/app.js"]. Debugging: distroless containers have no shell — use docker exec --entrypoint /busybox/sh with distroless debug variants (:debug tag), or use ephemeral debug containers in Kubernetes (kubectl debug).
09
What is Dockerfile heredoc syntax and when would you use it?
Dockerfile heredoc syntax (introduced in Dockerfile 1.4, enabled with BuildKit) allows writing multi-line strings directly in Dockerfile instructions without the awkward backslash-newline continuation. Two main uses: (1) Multi-line RUN scripts: instead of RUN command1 && \\n command2 && \\n command3, use: RUN <<EOF\ncommand1\ncommand2\ncommand3\nEOF. More readable, especially for complex shell scripts. Can specify the shell: RUN <<EOF bash\nset -e\napt-get update\napt-get install -y curl\nEOF; (2) Inline file creation: create configuration files directly in the Dockerfile instead of COPYing them from a separate file: COPY <<EOF /etc/nginx/conf.d/default.conf\nserver {\n listen 80;\n location / {\n proxy_pass http://app:3000;\n }\n}\nEOF. Also: RUN <<EOF python3\nimport os\nprint(os.environ.get("HOME"))\nEOF — run Python inline. Enable heredoc: add # syntax=docker/dockerfile:1 at the top of your Dockerfile (allows Dockerfile syntax versioning). Benefits: cleaner Dockerfiles, no need for separate config files that only exist for COPY, more readable multi-step commands.
10
How do you implement zero-downtime deployments with Docker?
Zero-downtime deployments update running containers without dropping requests. Strategies: (1) Docker Swarm rolling updates: docker service update --image myapp:2.0 --update-parallelism 1 --update-delay 30s --update-failure-action rollback myapp — updates one replica at a time, waits 30s between replicas, automatically rolls back if new containers fail health checks; (2) Blue-Green deployment: run two identical environments (blue=current, green=new). Deploy new version to green; run tests; switch the load balancer/reverse proxy to point to green; if issues, switch back to blue instantly. Containers: have two sets of containers with different labels/names; switch Nginx/HAProxy upstream or DNS; (3) Canary deployment: route a percentage of traffic to the new version (10%), monitor metrics, gradually increase percentage; (4) Reverse proxy integration: Nginx upstream reloads (nginx -s reload) are zero-downtime — Nginx finishes in-flight requests before removing old workers. In Docker Compose, update the service definition and run docker compose up -d — Compose recreates containers one at a time for services with multiple replicas; (5) Health check gates: use healthchecks to ensure new containers are ready before the old ones are removed. Application requirements: containers must handle SIGTERM gracefully (finish in-flight requests), implement readiness endpoints, and be stateless.
11
What is Compose watch and how does it improve developer experience?
Docker Compose Watch (introduced in Docker Compose 2.22 / Docker Desktop 4.24) is a developer experience feature that automatically syncs file changes from the host into running containers and can trigger service rebuilds — providing a live development loop without manually restarting services. Configure in compose.yml under each service: develop:\n watch:\n - action: sync\n path: ./src\n target: /app/src\n - action: rebuild\n path: ./package.json\n - action: sync+restart\n path: ./config\n target: /app/config. Actions: sync — copy changed files into the running container (like a live bind mount but more targeted); rebuild — trigger a docker compose build + recreate when specified files change (e.g., package.json changes → rebuild image with new dependencies); sync+restart — sync files then restart the container. Start with watch: docker compose watch (or docker compose up --watch). Benefits over bind mounts: (1) Works correctly on macOS (bind mounts on Mac suffer from performance and inotify issues); (2) More explicit control over what gets synced; (3) Can trigger rebuilds for dependency changes without syncing node_modules back; (4) Compatible with native file watchers in frameworks (nodemon, webpack HMR). Requires Docker Compose spec v2+ and BuildKit.