Q1: What is the difference between COPY and ADD in a Dockerfile?
COPY simply copies files from build context to the image — explicit and predictable. ADD does the same but also supports auto-extracting .tar.gz archives and fetching remote URLs. Best practice: use COPY always unless you specifically need ADD’s tar extraction feature.
Q2: What is a multi-stage build and why would you use it?
Multi-stage builds use multiple FROM statements. Earlier stages (build stage) compile/build the application; the final stage copies only the runtime artifacts, leaving behind all build tools and dependencies. Result: tiny, secure production images. For example, a TypeScript app might compile in a node:18 stage but only ship the dist/ output in an alpine image.
Q3: How does Docker layer caching work and how do you optimize for it?
Each Dockerfile instruction creates a cached layer. If an instruction and all preceding layers are unchanged, Docker reuses the cache. Optimization: order instructions from least-changed (base image, dependencies) to most-changed (source code). Always copy package.json and run npm install before COPY . ..
Q4: What is docker-compose depends_on and what are its limitations?
depends_on controls startup order — it starts services in dependency order. However, it does NOT wait for a service to be ready (only started). For database readiness, use condition: service_healthy with a healthcheck defined. Otherwise, your API might start before the DB accepts connections.
Q5: How do you manage secrets in Docker?
Three approaches:
Env files (--env-file .env) — simple but requires file security
Docker Secrets (Swarm mode) — secrets available as files at /run/secrets/
Q7: How would you reduce a Docker image from 900MB to under 100MB?
Four strategies:
Use Alpine base image (node:18-alpine vs node:18)
Multi-stage builds — copy only compiled output to final stage
npm ci --only=production — exclude devDependencies
.dockerignore — exclude node_modules, .git, test files
Chain RUN commands to reduce layers; clean up in same step
Q8: How do you implement zero-downtime deployments with Docker Compose?
Terminal window
# Pull new image
dockercomposepullapi
# Rolling update: start new container before stopping old
dockercomposeup-d--no-deps--scaleapi=2api
# Wait for new instance to be healthy
sleep10
dockercomposeup-d--no-deps--scaleapi=1api
For true zero-downtime, Nginx upstream health checks detect the new instance before traffic is routed.
Q9: What happens when you run docker build --no-cache?
All cached layers are ignored and every instruction is re-executed from scratch. Useful when you need the latest versions of packages from apt-get or apk that would otherwise be served from a stale cache.
Q10: Describe a production-grade Docker security hardening approach.
A layered approach:
Dockerfile: non-root user, specific version tags, minimal base image, multi-stage build
Runtime: --read-only, --cap-drop ALL, --security-opt no-new-privileges, memory/CPU limits
Network: internal networks for databases, no unnecessary port exposure
Registry: scan images with Trivy/Snyk in CI before deployment
Secrets: Docker Secrets or external vault — never env vars with sensitive values in plain text