For every Docker question, structure your answer with: what it is, why it matters, and a concrete example from your experience or a scenario you can walk through. Vague answers get filtered out — specifics win.
🎯 Docker Interview Preparation
All the Docker questions you will face in DevOps, SRE, and platform engineering interviews — from fundamentals to Senior/Staff-level architecture scenarios.
🟢 Fundamentals
Docker packages an application with all its dependencies into a portable container image that runs identically across environments. It solves the "works on my machine" problem by making the environment part of the artifact. This is critical for CI/CD pipelines where a build must be reproducible across developer laptops, CI runners, and cloud hosts.
An image is a read-only layered snapshot stored on disk. A container is a running instance of an image — it adds a writable layer and a running process. Multiple containers can run from the same image simultaneously. Image = class, container = object.
1. Docker CLI sends the run request to the Docker daemon via REST API. 2. Daemon checks if the nginx image is available locally. 3. If not, it pulls all layers from Docker Hub. 4. Daemon creates a container: a new writable layer on top of the nginx image layers. 5. Daemon starts the container process with the nginx root process as PID 1. 6. The container joins the default bridge network and gets an internal IP. 7. The CLI streams output (if not -d) or returns the container ID.
Each Dockerfile instruction creates a read-only layer identified by its content hash. Layers are stacked into an image — each container gets a thin writable layer on top. Layers are cached and shared: if two images share the same base (e.g., node:18-alpine), those layers are stored once on disk. Changing a layer invalidates all layers above it in the build cache, so ordering matters for build performance.
It prevents specified files and directories from being sent to the Docker build context. Without it, the daemon receives your entire project including node_modules, .git history, and .env secrets — slowing builds and risking secret leakage into images. Always include node_modules, .git, .env files, and test/coverage directories.
🟡 Intermediate
Multi-stage builds use multiple FROM instructions in one Dockerfile. Each stage has its own filesystem. You COPY artifacts from one stage to another using COPY --from=stagename. The final image only contains what is in the last stage — build tools, compilers, and dev dependencies are excluded. Result: production images go from 800MB+ to under 150MB, with much smaller attack surface.
Containers on the same user-defined bridge network can communicate using container names as hostnames, via Docker's embedded DNS server at 127.0.0.11. The default bridge does not support DNS — only IP addresses work there. Other drivers: host (shares host networking, no isolation), overlay (multi-host for Docker Swarm), none (no networking). For production, always use user-defined networks.
Use Docker volumes or bind mounts. Named volumes (docker volume create) are Docker-managed storage objects that persist until explicitly deleted — independent of container lifecycle. Bind mounts link a host directory into the container. For production databases, use named volumes. In Kubernetes, use PersistentVolumeClaims. Never rely on the container's writable layer for data that matters.
ENTRYPOINT sets the fixed executable that runs when the container starts. CMD provides default arguments to ENTRYPOINT, or acts as the default command if no ENTRYPOINT is set. CMD can be overridden with docker run <image> <override>. ENTRYPOINT requires --entrypoint to override. Best practice: ENTRYPOINT=["node"], CMD=["server.js"] — users can change the script without changing the executable.
1. docker ps -a — check exit code (1=app error, 137=OOM, 126=permission denied). 2. docker logs <id> — read the crash output. 3. docker inspect <id> | grep OOMKilled — check for OOM. 4. Override entrypoint docker run -it --entrypoint /bin/sh <image> — run app manually for full error. 5. Check required environment variables are set. 6. Verify file permissions inside the container.
🔴 Advanced & Scenario-Based
1. GitHub Push triggers GitHub Actions workflow. 2. Build: docker buildx build with BuildKit cache from GHA cache. 3. Scan: Trivy scans image — fail pipeline on CRITICAL CVEs. 4. Tag: git SHA for immutable traceability, semver for releases. 5. Push to ACR: authenticate via az acr login using service principal secrets. 6. Deploy to AKS dev namespace: kubectl set image on Deployment. 7. Promote to staging/prod by updating Deployment image tags via Helm upgrade or GitOps (Flux/ArgoCD). AKS uses managed identity for ACR pull — no imagePullSecrets.
Never hardcode secrets in Dockerfiles or ENV instructions — they end up in image layers. Options by context: 1) Build-time secrets: use Docker BuildKit --secret id=mysecret,src=.env — the secret is available during the build RUN step but NOT in the final image. 2) Runtime: inject via environment variables from Kubernetes secrets, Azure Key Vault CSI driver, or ACI environment variable injection. 3) Application-level: use Azure Managed Identity to fetch secrets from Key Vault at runtime — no static credentials at all. Always rotate if leaked.
1. Security context: K8s may enforce runAsNonRoot=true — check if your image has a non-root USER. 2. Read-only filesystem: K8s pods with readOnlyRootFilesystem=true need tmpfs mounts for any writable paths. 3. Resource limits: K8s enforces CPU/memory limits strictly — OOMKilled or CPU throttling may not appear locally. 4. Health probes: misconfigured liveness probe can kill the container before it finishes starting. 5. Environment variables: K8s provides vars via ConfigMaps/Secrets — not from your local .env. 6. Image tag: K8s runs exactly what is specified — check no stale :latest tag is being pulled.
1. Switch from full OS base (node:18) to Alpine (node:18-alpine) — saves ~200MB. 2. Multi-stage build: compile in builder stage, copy only output binary + production deps to final stage — removes compilers, test frameworks, dev packages. 3. Use npm ci --only=production to exclude devDependencies. 4. Consolidate RUN commands to reduce layer count. 5. Use distroless base instead of Alpine for the final stage — minimal runtime only. 6. Use .dockerignore to prevent test files, docs, and source maps from entering the image. 7. Analyze with docker image history or diving tool to find the biggest layers.
📋 Quick-Fire Cheatsheet
# Must-know answers for any Docker interview: Image vs Container → Image = read-only template; Container = running instance Layer caching → stable layers first (deps before code) Non-root user → always; use adduser + USER in Dockerfile CMD vs ENTRYPOINT → CMD = overridable default; ENTRYPOINT = fixed executable Volume vs Bind Mount → Volume = Docker-managed; Bind Mount = host path Exit 137 → OOMKilled — increase --memory or fix memory leak :latest in prod → never — use git SHA or semver tags Multi-stage → build tools in stage 1, only output in final stage ACR + AKS → attach with --attach-acr; uses managed identity; no pull secrets Health check → HEALTHCHECK CMD + /healthz endpoint + liveness probe in K8s .dockerignore → exclude node_modules, .env, .git, *.log