Docker Interview Questions
Docker Interview Questions & Answers (1–60)
Section titled “Docker Interview Questions & Answers (1–60)”Section 1: Docker Basics
Section titled “Section 1: Docker Basics”Q1: What is Docker and how does it differ from a Virtual Machine?
Section titled “Q1: What is Docker and how does it differ from a Virtual Machine?”Answer: Docker is an open-source platform that packages applications and their dependencies into lightweight, portable containers.
| Aspect | Docker (Container) | Virtual Machine |
|---|---|---|
| OS | Shares host OS kernel | Full guest OS |
| Size | MBs | GBs |
| Startup | Seconds | Minutes |
| Isolation | Process-level (namespaces) | Hardware-level (hypervisor) |
| Performance | Near-native | 5-15% overhead |
| Portability | ✅ Run anywhere Docker runs | ❌ Hypervisor-dependent |
| Use case | Microservices, CI/CD | Full OS isolation, legacy apps |
VM Architecture: Container Architecture:┌─────────────┐ ┌────────┬────────┐│ App A │ │ App A │ App B ││ Libs │ │ Libs │ Libs ││ Guest OS │ ├────────┴────────┤├─────────────┤ │ Docker Engine ││ Hypervisor │ ├─────────────────┤├─────────────┤ │ Host OS ││ Host OS │ └─────────────────┘└─────────────┘Key Points:
- Containers share the host OS kernel — no guest OS needed
- Docker uses Linux namespaces (isolation) and cgroups (resource limits)
- Containers start in seconds; VMs take minutes
Interview tip: “Docker is not a full VM — it’s a process on your host with isolated filesystem, network, and PID namespace. The host kernel is shared.”
Q2: What are Docker images and containers?
Section titled “Q2: What are Docker images and containers?”Answer:
| Docker Image | Docker Container | |
|---|---|---|
| What | Read-only blueprint/template | Running instance of an image |
| State | Static (immutable) | Has mutable read-write layer |
| Created by | docker build | docker run |
| Stored | Registry or local disk | Local only |
| Analogy | Class (OOP) | Object/Instance (OOP) |
# Image lifecycledocker pull nginx:alpine # Download from registrydocker images # List local imagesdocker rmi nginx:alpine # Remove imagedocker image prune # Remove unused images
# Container lifecycledocker run -d --name my-nginx -p 80:80 nginx:alpine # Create + startdocker stop my-nginx # Stop (sends SIGTERM)docker start my-nginx # Restart stopped containerdocker rm my-nginx # Delete stopped containerdocker rm -f my-nginx # Force delete running container
# Inspectdocker ps # Running containersdocker ps -a # All containers (including stopped)docker inspect my-nginx # Full details (IP, mounts, env)Common mistake:
docker rmiremoves the IMAGE but not containers.docker rmremoves CONTAINERS. Usedocker system pruneto clean everything.
Q3: What is a Dockerfile and what are common instructions?
Section titled “Q3: What is a Dockerfile and what are common instructions?”Answer: A Dockerfile is a text file with instructions to build a Docker image. Each instruction creates a new layer.
# ---- Production-ready Dockerfile example ----
# Base image (always pin a specific version)FROM node:18-alpine
# Set working directory (creates dir if not exists)WORKDIR /app
# Copy package files FIRST (layer cache optimization)COPY package*.json ./
# Install dependenciesRUN npm ci --omit=dev
# Copy source code AFTER install (changes more often)COPY . .
# Build-time argument (not available at runtime)ARG GIT_COMMIT=unknownLABEL git-commit=$GIT_COMMIT
# Runtime environment variableENV NODE_ENV=production
# Create non-root user (security best practice)RUN addgroup -S appgroup && adduser -S appuser -G appgroupUSER appuser
# Document the port (does NOT publish it)EXPOSE 3000
# Define volume mount pointVOLUME ["/app/data"]
# Health checkHEALTHCHECK --interval=30s --timeout=5s \ CMD wget -qO- http://localhost:3000/health || exit 1
# Default command when container startsCMD ["node", "server.js"]Common Instructions:
| Instruction | Runs at | Purpose | Creates layer? |
|---|---|---|---|
FROM | Build | Base image | ✅ Yes |
RUN | Build | Execute shell command | ✅ Yes |
COPY | Build | Copy files from host | ✅ Yes |
ADD | Build | Like COPY + URL + tar extract | ✅ Yes |
WORKDIR | Build | Set working directory | ✅ Yes |
ENV | Build + Runtime | Set environment variable | ✅ Yes |
ARG | Build only | Build-time variable | ❌ No |
EXPOSE | Documentation | Document port (doesn’t publish) | ❌ No |
CMD | Runtime | Default command (overrideable) | ❌ No |
ENTRYPOINT | Runtime | Fixed executable | ❌ No |
USER | Build + Runtime | Switch to this user | ❌ No |
VOLUME | Runtime | Mount point | ❌ No |
HEALTHCHECK | Runtime | Container health check | ❌ No |
LABEL | Build | Metadata | ❌ No |
Interview tip:
EXPOSEdoes NOT open a port on the host — it’s just documentation. You still need-p 3000:3000when running.
Q4: What is the difference between CMD and ENTRYPOINT?
Section titled “Q4: What is the difference between CMD and ENTRYPOINT?”Answer:
| Feature | CMD | ENTRYPOINT |
|---|---|---|
| Override | docker run myimage <new-cmd> | --entrypoint flag only |
| Purpose | Default arguments | Fixed executable |
| Combined | CMD becomes args to ENTRYPOINT | ENTRYPOINT is always run |
# CMD only — entire command is replaceableCMD ["nginx", "-g", "daemon off;"]# docker run myimage ls -la ← completely replaces CMD
# ENTRYPOINT only — always runs nginxENTRYPOINT ["nginx"]# docker run myimage -g "daemon off;" ← -g becomes arg to nginx
# ENTRYPOINT + CMD (best pattern)ENTRYPOINT ["nginx"]CMD ["-g", "daemon off;"] ← default args, easily replaced# docker run myimage -c /etc/nginx/custom.conf ← replaces CMD part only# Examples at runtime:docker run myimage # → runs ENTRYPOINT + CMD defaultsdocker run myimage --debug # → runs ENTRYPOINT + ["--debug"]docker run --entrypoint /bin/sh myimage # → overrides ENTRYPOINT (for debugging)Shell form vs Exec form:
# Shell form (BAD for ENTRYPOINT — wraps in /bin/sh, can't receive signals)CMD node server.js
# Exec form (GOOD — direct exec, receives SIGTERM properly)CMD ["node", "server.js"]Interview tip: Always use exec form
["cmd", "arg"]for ENTRYPOINT and CMD. Shell form wraps in/bin/sh -c, which means PID 1 is the shell, not your app — signals like SIGTERM won’t reach your app, causing slow shutdowns.
Q5: How do you build and tag a Docker image?
Section titled “Q5: How do you build and tag a Docker image?”Answer:
# Basic builddocker build -t myapp:1.0 .
# Build with multiple tags in one commanddocker build -t myapp:1.0 -t myapp:latest .
# Use specific Dockerfiledocker build -f Dockerfile.prod -t myapp:prod .
# Pass build-time variables (available only during build)docker build --build-arg NODE_ENV=production --build-arg GIT_SHA=$(git rev-parse --short HEAD) -t myapp:1.0 .
# Build from different context directorydocker build -t myapp:1.0 -f ./docker/Dockerfile ./src
# No cache (useful in CI to get fresh dependencies)docker build --no-cache -t myapp:1.0 .
# View layer sizes and commandsdocker history myapp:1.0docker history --no-trunc myapp:1.0 # Full commandsInterview tip: In CI/CD, tag with git SHA for immutability:
myapp:abc1234. Also tagmyapp:latestfor convenience. Never deploy:latestto production — it’s not reproducible.
Q6: How do you push an image to Docker Hub?
Section titled “Q6: How do you push an image to Docker Hub?”Answer:
# Docker Hubdocker login # Prompts username/passworddocker login -u $USERNAME -p $PASSWORD # Non-interactive (CI)docker tag myapp:1.0 username/myapp:1.0docker push username/myapp:1.0
# AWS ECRaws ecr get-login-password --region us-east-1 | \ docker login --username AWS \ --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.comdocker tag myapp:1.0 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0
# Azure ACRaz acr login --name myregistrydocker tag myapp:1.0 myregistry.azurecr.io/myapp:1.0docker push myregistry.azurecr.io/myapp:1.0
# Google GCRgcloud auth configure-dockerdocker push gcr.io/my-project/myapp:1.0Common mistake: Never use
docker login -pwith your actual password in shell scripts — it shows up in shell history. Use--password-stdininstead.
Section 2: Docker Networking
Section titled “Section 2: Docker Networking”Q7: What are Docker network types?
Section titled “Q7: What are Docker network types?”Answer:
| Network | Description | DNS by name | Multi-host | Use Case |
|---|---|---|---|---|
bridge (default) | Containers on docker0 | ❌ No | ❌ No | Quick testing only |
bridge (user-defined) | Custom bridge | ✅ Yes | ❌ No | Single-host apps |
host | Uses host network directly | N/A | N/A | Maximum performance |
none | No network at all | N/A | N/A | Security-sensitive tasks |
overlay | Multi-host virtual network | ✅ Yes | ✅ Yes | Docker Swarm |
macvlan | Container gets own MAC/IP | ✅ Yes | ✅ Yes | Legacy apps needing real IPs |
# List networksdocker network ls
# Create custom bridge network (recommended for dev)docker network create --driver bridge my-app-net
# Run container on networkdocker run -d --network my-app-net --name api myapp:1.0docker run -d --network my-app-net --name db postgres:15
# API can reach DB by name: http://db:5432# DB can reach API by name: http://api:3000
# Inspect network and see connected containersdocker network inspect my-app-net
# Connect existing running container to another networkdocker network connect my-app-net my-container
# Disconnectdocker network disconnect my-app-net my-container
# Remove unused networksdocker network pruneInterview tip: Always use user-defined bridge networks instead of the default bridge. The default bridge doesn’t support DNS resolution by container name — you’d have to use IPs which change on restart.
Q8: How do containers communicate with each other?
Section titled “Q8: How do containers communicate with each other?”Answer:
# 1. Using user-defined bridge network (most common)docker network create app-net
docker run -d --name postgres --network app-net \ -e POSTGRES_PASSWORD=secret postgres:15
docker run -d --name api --network app-net \ -e DATABASE_URL=postgresql://postgres:secret@postgres:5432/mydb \ myapp:1.0# 'api' container can reach 'postgres' by container name
# Test from inside container:docker exec api ping postgres # DNS works by name!docker exec api nc -zv postgres 5432 # Check port open
# 2. Using Docker Compose (auto-creates network)# All services in same compose file can reach each other by service name
# 3. Using container IP (NOT recommended - IPs change)docker inspect postgres --format '{{.NetworkSettings.Networks.app-net.IPAddress}}'# → 172.20.0.2 ← unreliable, use name insteadHow Docker DNS works:
Docker embedded DNS server runs at 127.0.0.11When container does: curl http://db:5432 → Container's /etc/resolv.conf points to 127.0.0.11 → Docker DNS resolves 'db' → 172.20.0.3 → Request reaches db containerCommon mistake: This only works on user-defined networks. The default
docker0bridge does NOT have embedded DNS.
Q9: How do you expose and publish container ports?
Section titled “Q9: How do you expose and publish container ports?”Answer:
# Syntax: -p <host_port>:<container_port>docker run -p 8080:80 nginx# ↑ ↑# host port container port# Access via: http://localhost:8080
# Bind to specific host IP (security — only localhost)docker run -p 127.0.0.1:8080:80 nginx# Only accessible from same machine, NOT from network
# Multiple portsdocker run -p 80:80 -p 443:443 nginx
# Random host port (Docker picks)docker run -P nginx # Publishes all EXPOSE'd ports to random ports
# Check what ports are mappeddocker port my-containerdocker ps # Shows port mappings in output
# UDP portsdocker run -p 53:53/udp dns-serverEXPOSE vs -p:
# EXPOSE in Dockerfile — just documentation, does NOT publishEXPOSE 3000# -p flag actually publishes (opens host port)docker run -p 3000:3000 myapp # ← This actually opens the portInterview tip:
EXPOSEis just metadata — it documents which port the container listens on. You still need-pto make it accessible from the host. Think ofEXPOSEas the contract for what port to use with-p.
Section 3: Docker Volumes & Storage
Section titled “Section 3: Docker Volumes & Storage”Q10: What are Docker volumes and when to use them?
Section titled “Q10: What are Docker volumes and when to use them?”Answer: Docker provides three storage options:
| Type | How | Where stored | Best For |
|---|---|---|---|
| Named Volume | -v myvolume:/container/path | /var/lib/docker/volumes/ | Production databases, persistent data |
| Bind Mount | -v /host/path:/container/path | Anywhere on host | Dev with live code reload |
| tmpfs | --tmpfs /container/path | RAM only (not disk) | Secrets, temp data, performance |
# Named Volume (production — Docker manages it)docker volume create db-datadocker run -d \ --name postgres \ -v db-data:/var/lib/postgresql/data \ postgres:15# Data persists even after container is deleted!
# Bind Mount (development — see changes instantly)docker run -d \ -v $(pwd)/src:/app/src \ -v $(pwd)/public:/app/public \ -p 3000:3000 \ node:18# Edit files locally → instantly reflected in container
# tmpfs (in-memory, fast, gone on stop)docker run --tmpfs /tmp:rw,noexec,nosuid,size=100m myapp
# Read-only bind mount (config files, never write)docker run -v $(pwd)/config.json:/app/config.json:ro myapp
# Inspect and manage volumesdocker volume lsdocker volume inspect db-datadocker volume prune # Remove volumes not used by any containerCommon mistake: Bind mounts depend on the host path existing. Named volumes are portable and can be backed up with
docker volume. On production, always use named volumes for databases — never bind mounts.
Q11: How do you back up and restore Docker volumes?
Section titled “Q11: How do you back up and restore Docker volumes?”Answer:
# Backup volume to tar archivedocker run --rm \ -v db-data:/data:ro \ -v $(pwd)/backups:/backup \ alpine \ tar czf /backup/db-backup-$(date +%Y%m%d).tar.gz -C /data .
# Restore volume from backupdocker run --rm \ -v db-data:/data \ -v $(pwd)/backups:/backup \ alpine \ tar xzf /backup/db-backup-20240115.tar.gz -C /data
# Copy files between host and container (quick method)docker cp my-container:/app/config.json ./config.json # container → hostdocker cp ./config.json my-container:/app/config.json # host → container
# Postgres-specific backup (recommended over volume backup)docker exec postgres-container \ pg_dump -U postgres mydb > backup.sql
# Restore postgrescat backup.sql | docker exec -i postgres-container \ psql -U postgres mydb
# Move volume between hosts# 1. Exportdocker run --rm -v myvolume:/data alpine tar czf - /data | ssh user@newhost 'docker run --rm -v myvolume:/data -i alpine tar xzf - -C /'Interview tip: For database backups, use the database’s native tools (
pg_dump,mysqldump) rather than backing up the raw volume. Raw volume backups can be corrupted if the DB was writing during backup.
Section 4: Docker Compose
Section titled “Section 4: Docker Compose”Q12: What is Docker Compose and how does it work?
Section titled “Q12: What is Docker Compose and how does it work?”Answer: Docker Compose defines and runs multi-container applications using a YAML file.
version: '3.9'
services: web: build: . ports: - "3000:3000" environment: - NODE_ENV=production - DB_HOST=db depends_on: db: condition: service_healthy restart: unless-stopped
db: image: postgres:15 environment: POSTGRES_DB: myapp POSTGRES_USER: admin POSTGRES_PASSWORD: secret volumes: - postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U admin"] interval: 10s timeout: 5s retries: 5
redis: image: redis:7-alpine ports: - "6379:6379"
volumes: postgres-data:
networks: default: driver: bridge# Start all servicesdocker compose up -d
# View logsdocker compose logs -f
# Scale a servicedocker compose up -d --scale web=3
# Stop and remove containers/networksdocker compose down
# Also remove volumesdocker compose down -vQ13: How do you use environment variables in Docker Compose?
Section titled “Q13: How do you use environment variables in Docker Compose?”Answer:
# .env file (auto-loaded by Compose)DB_PASSWORD=supersecretAPP_PORT=3000IMAGE_TAG=1.2.0services: app: image: myapp:${IMAGE_TAG} ports: - "${APP_PORT}:3000" environment: - DB_PASSWORD=${DB_PASSWORD} env_file: - .env.production# Override with inline envDB_PASSWORD=test docker compose up
# Use alternate env filedocker compose --env-file .env.staging upSection 5: Multi-stage Builds & Optimization
Section titled “Section 5: Multi-stage Builds & Optimization”Q14: What are multi-stage builds and why use them?
Section titled “Q14: What are multi-stage builds and why use them?”Answer: Multi-stage builds use multiple FROM statements to produce a smaller final image by separating build-time dependencies from runtime.
# Stage 1: BuildFROM node:18 AS builderWORKDIR /appCOPY package*.json ./RUN npm ciCOPY . .RUN npm run build
# Stage 2: Production image (much smaller)FROM node:18-alpine AS productionWORKDIR /appENV NODE_ENV=production
COPY --from=builder /app/dist ./distCOPY --from=builder /app/package*.json ./RUN npm ci --omit=dev
EXPOSE 3000USER nodeCMD ["node", "dist/server.js"]# Build specific stagedocker build --target builder -t myapp:build .docker build --target production -t myapp:prod .Result: Build image may be 800MB; production image ~120MB.
Q15: How do you optimize Docker image size?
Section titled “Q15: How do you optimize Docker image size?”Answer:
# 1. Use minimal base imagesFROM alpine:3.18 # ~5MB vs ubuntu ~77MBFROM node:18-alpine # ~170MB vs node:18 ~1GB
# 2. Combine RUN commands to reduce layersRUN apt-get update && \ apt-get install -y curl wget && \ rm -rf /var/lib/apt/lists/*
# 3. Use .dockerignore# .dockerignorenode_modules.git*.logdist.env
# 4. Order layers by change frequency (cache optimization)COPY package*.json ./ # Changes rarelyRUN npm install # Cached if package.json unchangedCOPY . . # Changes often# Inspect image layers and sizesdocker history myapp:1.0
# Use dive tool for layer analysisdive myapp:1.0Section 6: Docker Security
Section titled “Section 6: Docker Security”Q16: What are Docker security best practices?
Section titled “Q16: What are Docker security best practices?”Answer:
1. Run as non-root user
# Create and use non-root user (most important security step)FROM node:18-alpineRUN addgroup -S appgroup && adduser -S appuser -G appgroupUSER appuser # All subsequent commands and the container run as appuser2. Read-only filesystem + writable tmpfs
docker run \ --read-only \ --tmpfs /tmp:rw,noexec,nosuid \ myapp# Root FS is immutable; only /tmp is writable3. Drop all Linux capabilities
docker run \ --cap-drop ALL \ --cap-add NET_BIND_SERVICE \ --security-opt no-new-privileges:true \ myapp# Container can't gain new privileges (prevents setuid exploits)4. Pin image versions
# BAD — :latest changes silentlyFROM node:latest
# GOOD — exact versionFROM node:18.19.1-alpine3.19
# BEST — pin by digest (immutable)FROM node@sha256:abc123...def4565. Scan for vulnerabilities
# Trivy (most popular)trivy image --exit-code 1 --severity CRITICAL,HIGH myapp:1.0
# Docker Scout (built-in)docker scout cves myapp:1.0docker scout recommendations myapp:1.0 # Suggests safer base images
# Grypegrype myapp:1.06. Resource limits (prevent DoS)
docker run \ --memory=512m \ --memory-swap=512m \ --cpus=1.0 \ --pids-limit=100 \ --ulimit nofile=1024:1024 \ myappInterview tip: The #1 security issue in Docker is running containers as root. Even if your app doesn’t need root, a vulnerability could be exploited to escape to the host. Always add
USERin Dockerfile.
Q17: How do you manage Docker secrets?
Section titled “Q17: How do you manage Docker secrets?”Answer:
| Method | Security | Visible in inspect? | Use Case |
|---|---|---|---|
ENV in Dockerfile | ❌ Worst | ✅ Yes | Never use for secrets |
-e flag at runtime | ⚠️ Medium | ✅ Yes | Only for non-sensitive values |
.env file | ⚠️ Medium | ✅ Yes | Dev only |
| Docker Swarm secrets | ✅ Good | ❌ No | Swarm deployments |
| External vault (Vault/SSM) | ✅ Best | ❌ No | Production |
# Docker Swarm secrets (stored encrypted in Swarm Raft)echo "supersecret" | docker secret create db_password -docker secret ls
# Use in servicedocker service create \ --name db \ --secret db_password \ -e POSTGRES_PASSWORD_FILE=/run/secrets/db_password \ postgres:15# Secret file appears at /run/secrets/db_password inside container# docker-compose with Swarm secretsversion: '3.9'services: db: image: postgres:15 secrets: - db_password environment: POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets: db_password: external: true # Already created with docker secret create# Without Swarm — best practice: inject at runtime from external source# Using AWS Secrets Manager:docker run \ -e DB_PASSWORD=$(aws secretsmanager get-secret-value \ --secret-id /prod/db-password --query SecretString --output text | \ jq -r '.password') \ myapp
# Using HashiCorp Vault:DB_PASS=$(vault kv get -field=password secret/myapp/db)docker run -e DB_PASSWORD=$DB_PASS myappCommon mistake: Putting secrets in Dockerfile
ENVmeans they’re baked into every image layer and visible to anyone withdocker inspectordocker history. Always inject secrets at runtime.
Section 7: Docker Troubleshooting
Section titled “Section 7: Docker Troubleshooting”Q18: How do you debug a failing container?
Section titled “Q18: How do you debug a failing container?”Answer:
# Step 1: Check what happeneddocker ps -a # See all containers (including stopped ones)docker logs container_name # View stdout/stderrdocker logs --tail 50 -f container_name # Stream last 50 linesdocker logs --since 10m container_name # Last 10 minutes only
# Step 2: Check exit code and statedocker inspect container_name --format='{{.State.ExitCode}}'docker inspect container_name --format='{{.State.Error}}'docker inspect container_name | grep -A5 '"State"'
# Step 3: Enter running container for live debuggingdocker exec -it container_name /bin/sh # sh (alpine/minimal images)docker exec -it container_name bash # bash (debian/ubuntu)docker exec -it container_name env # Check environment variables
# Step 4: Debug a crashed container (can't exec into it)docker run -it --entrypoint /bin/sh myapp:1.0 # Override entrypoint# Now manually run: node server.js and see the error
# Step 5: Check resource issuesdocker stats container_name --no-streamdocker inspect container_name | grep -i oom # OOM killed?
# Step 6: Check events (useful for start/stop cycles)docker events --filter container=container_name
# Step 7: Network debugging from inside containerdocker exec container_name wget -qO- http://other-service:8080/healthdocker exec container_name nslookup other-service # DNS checkInterview tip: Start with
docker logsthendocker inspect. If the container keeps crashing (restart loop), usedocker run -it --entrypoint shto start a shell and manually run the command to see the error.
Q19: Container starts and immediately exits — how do you debug?
Section titled “Q19: Container starts and immediately exits — how do you debug?”Answer: This is one of the most common Docker problems. Follow this checklist:
# 1. Check logs FIRSTdocker logs container_name# Look for error messages, stack traces, missing env vars
# 2. Check exit codedocker inspect container_name --format='{{.State.ExitCode}}'| Exit Code | Cause | Fix |
|---|---|---|
0 | Intentional exit (CMD completed) | App ran and finished (for jobs, this is OK) |
1 | Application error/crash | Check logs for exception |
125 | Docker error | Check docker command syntax |
126 | Permission denied to CMD | Check file permissions |
127 | CMD not found | Check CMD path in Dockerfile |
137 | OOM killed or docker kill | Increase memory limit |
143 | SIGTERM (graceful stop) | Normal shutdown |
# 3. Run interactively to see error output directlydocker run -it myapp:1.0
# 4. Override entrypoint to enter shell and debugdocker run --entrypoint /bin/sh -it myapp:1.0# Now manually run your command: node server.js
# 5. Common causes:# - App script not executable: chmod +x entrypoint.sh# - Wrong CMD path: CMD ["/app/server"] but file is at /app/bin/server# - Missing required env var: DB_URL not set → crash# - Port already in use on host# - Config file not found
# 6. Check OOMdocker inspect container_name | grep OOMKilled# true → add --memory limit or fix memory leakInterview tip: Always check
docker logsfirst. 90% of the time the answer is right there.
Q20: How do you monitor Docker containers?
Section titled “Q20: How do you monitor Docker containers?”Answer:
# Real-time resource stats (like top but for containers)docker statsdocker stats --no-stream # One-time snapshotdocker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
# Check configured resource limitsdocker inspect container_name | grep -A5 'HostConfig' | grep -i 'memory\|cpu'Full monitoring stack with docker-compose:
services: cadvisor: image: gcr.io/cadvisor/cadvisor:latest ports: - "8080:8080" volumes: - /:/rootfs:ro - /var/run:/var/run:ro - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro privileged: true
prometheus: image: prom/prometheus:latest ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana: image: grafana/grafana:latest ports: - "3000:3000" environment: - GF_SECURITY_ADMIN_PASSWORD=admin# Check health status of containerdocker inspect container_name --format='{{.State.Health.Status}}'# healthy | unhealthy | starting
# View health check historydocker inspect container_name | jq '.[0].State.Health.Log'
# List containers by statusdocker ps --filter "health=unhealthy"docker ps --filter "status=exited"Interview tip:
docker statsis your first tool for CPU/memory debugging. For production, pair cAdvisor (metrics collector) + Prometheus (metrics storage) + Grafana (dashboards).
Section 8: Advanced Docker Topics
Section titled “Section 8: Advanced Docker Topics”Q21: What is Docker Swarm vs Kubernetes?
Section titled “Q21: What is Docker Swarm vs Kubernetes?”Answer:
| Aspect | Docker Swarm | Kubernetes |
|---|---|---|
| Setup | docker swarm init (1 command) | kubeadm/eksctl (complex) |
| Learning curve | Low | High |
| Scaling | docker service scale | HPA + manual |
| Auto-healing | ✅ Yes (basic) | ✅ Yes (advanced) |
| Load balancing | Built-in (VIP routing) | Requires Ingress controller |
| Storage | Named volumes | PV/PVC/StorageClass |
| Config | Secrets/Configs | ConfigMap/Secrets |
| Networking | Overlay network | CNI plugins (Calico, Flannel) |
| Community/ecosystem | Smaller, declining | Massive, growing |
| Enterprise adoption | Small-medium teams | Large enterprise |
# Docker Swarm — setupdocker swarm init --advertise-addr 192.168.1.10# On worker nodes:docker swarm join --token <token> 192.168.1.10:2377
# Deploy replicated servicedocker service create \ --name web \ --replicas 3 \ -p 80:80 \ --update-parallelism 1 \ --update-delay 10s \ nginx:alpine
# Scaledocker service scale web=5
# Rolling updatedocker service update --image nginx:1.25 web
# View service statusdocker service lsdocker service ps webdocker service logs web -fInterview tip: “Swarm is Docker’s built-in orchestrator — simpler but less powerful. In 2024, most companies use Kubernetes. Choose Swarm if you need quick orchestration without the K8s learning curve.”
Q22: How does Docker handle logging?
Section titled “Q22: How does Docker handle logging?”Answer:
| Driver | Where logs go | Use Case |
|---|---|---|
json-file | Local JSON files (default) | Dev, simple single-host |
local | Compressed local files | More efficient than json-file |
syslog | System syslog | On-premise Linux |
journald | systemd journal | systemd-based systems |
fluentd | Fluentd daemon | EFK stack |
awslogs | AWS CloudWatch Logs | AWS deployments |
gcplogs | Google Cloud Logging | GCP deployments |
splunk | Splunk HTTP event collector | Enterprise |
none | Disabled | Security-sensitive containers |
# Check current logging driverdocker info | grep "Logging Driver"
# Set per-container with rotation (production best practice)docker run \ --log-driver=json-file \ --log-opt max-size=10m \ --log-opt max-file=3 \ --log-opt compress=true \ myapp
# AWS CloudWatchdocker run \ --log-driver=awslogs \ --log-opt awslogs-group=/docker/myapp \ --log-opt awslogs-region=us-east-1 \ --log-opt awslogs-stream=web-1 \ myapp
# Set default for ALL containers in daemon.json# /etc/docker/daemon.json{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" }}# Then: sudo systemctl restart dockerCommon mistake: Default
json-filelogging has no size limit — logs can fill up your disk. Always setmax-sizeandmax-filein production.
Q23: What is a Docker registry and how do you run a private one?
Section titled “Q23: What is a Docker registry and how do you run a private one?”Answer: A Docker registry is a server that stores and distributes Docker images.
Cloud-managed registries (recommended for production):
| Registry | Provider | Free tier |
|---|---|---|
| Docker Hub | Docker | 1 private repo |
| ECR | AWS | Per-storage cost |
| ACR | Azure | Per-storage cost |
| GCR/Artifact Registry | Per-storage cost | |
| GitHub Container Registry (ghcr.io) | GitHub | Free for public |
# Run local private registry (for development/air-gapped)docker run -d \ --name local-registry \ -p 5000:5000 \ --restart always \ -v registry-data:/var/lib/registry \ registry:2
# Push to local registrydocker tag myapp:1.0 localhost:5000/myapp:1.0docker push localhost:5000/myapp:1.0
# Pull from local registrydocker pull localhost:5000/myapp:1.0
# Secure with TLS and authenticationdocker run -d \ --name secure-registry \ -p 443:5000 \ -v /certs:/certs \ -v /auth:/auth \ -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \ -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \ -e REGISTRY_AUTH=htpasswd \ -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \ -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \ registry:2
# List images in registrycurl http://localhost:5000/v2/_catalog# {"repositories":["myapp","nginx"]}
# List tags for an imagecurl http://localhost:5000/v2/myapp/tags/listInterview tip: For production, always use a managed registry (ECR, ACR, GCR) — they handle redundancy, scanning, and access control. Self-hosted registries are mainly for air-gapped environments.
Q24: How do you implement health checks in Docker?
Section titled “Q24: How do you implement health checks in Docker?”Answer: Health checks tell Docker whether your container is working correctly. Docker Swarm and Compose use health status to route traffic and restart unhealthy containers.
# In Dockerfile# Parameters:# --interval: how often to run (default 30s)# --timeout: max time for check (default 30s)# --start-period: grace period on start (default 0s) — crucial!# --retries: fail after this many consecutive failures (default 3)
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD curl -f http://localhost:3000/health || exit 1
# Or use wget (available in alpine when curl isn't)HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD wget -qO- http://localhost:3000/health || exit 1
# For databases: check if server is readyHEALTHCHECK --interval=10s --timeout=3s --start-period=10s --retries=5 \ CMD pg_isready -U postgres || exit 1# In docker-compose.ymlservices: api: image: myapp:1.0 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/health"] interval: 30s timeout: 5s start_period: 40s # App may take 40s to start — don't fail early! retries: 3 # depends_on with condition — waits for healthy status frontend: depends_on: api: condition: service_healthy # Waits for api to be healthy!# Check health statusdocker inspect --format='{{.State.Health.Status}}' container_name# healthy | unhealthy | starting
# View health check history (last 5 checks)docker inspect container_name | jq '.[0].State.Health'
# List unhealthy containersdocker ps --filter "health=unhealthy"Common mistake: Forgetting
start_period. If your app takes 30 seconds to start, health checks will fail 3 times and mark containerunhealthybefore it even starts. Always setstart_periodto your app’s typical startup time.
Q25: Explain Docker layer caching and how to leverage it.
Section titled “Q25: Explain Docker layer caching and how to leverage it.”Answer: Docker caches each instruction as a layer. Once ANY layer changes, ALL subsequent layers are rebuilt (cache miss).
# ❌ WRONG — npm install runs on EVERY code changeCOPY . . # Changes every timeRUN npm install # Always re-runs = slow!
# ✅ CORRECT — npm install only re-runs when package.json changesCOPY package*.json ./ # Rarely changes → stays cachedRUN npm install # Cached if package.json unchanged!COPY . . # Changes often — but this is the LAST expensive stepGeneral rule: Order by change frequency (least frequent → most frequent)
FROM node:18-alpine # Almost never changes ✅ cachedWORKDIR /app # Never changes ✅ cachedCOPY package*.json ./ # Changes occasionally ✅ usually cachedRUN npm ci # Expensive! cached when above unchanged ✅COPY . . # Changes every commit ❌ always rebuildsRUN npm run build # Only runs when code changes# Force rebuild without cache (useful in CI)docker build --no-cache -t myapp:1.0 .
# BuildKit persistent cache (speeds up repeated CI builds)# .dockerignore + BuildKit cache mountRUN --mount=type=cache,target=/root/.npm \ npm ci --prefer-offline# npm cache persists between builds → faster!
RUN --mount=type=cache,target=/root/.cache/pip \ pip install -r requirements.txt# pip cache persists too!Interview tip: Layer cache is invalidated by content change, not filename. If
package.jsonis unchanged,RUN npm installuses the cache even across different build machines IF using a shared cache (e.g., GitHub Actions cache). This is one of the biggest performance wins in CI/CD.
Q26: What is BuildKit and what are its advantages?
Section titled “Q26: What is BuildKit and what are its advantages?”Answer: BuildKit is Docker’s next-generation image build engine.
# Enable BuildKitexport DOCKER_BUILDKIT=1
# Or in daemon.json{ "features": { "buildkit": true }}
# Build with BuildKitdocker buildx build -t myapp:1.0 .# BuildKit features# 1. Parallel stage buildsFROM base AS stage1FROM base AS stage2
# 2. Cache mountsRUN --mount=type=cache,target=/root/.cache/pip \ pip install -r requirements.txt
# 3. Secret mounts (never stored in image)RUN --mount=type=secret,id=mysecret \ cat /run/secrets/mysecret > /app/secret
# 4. SSH mounts for private reposRUN --mount=type=ssh \ git clone git@github.com:org/private-repo.gitQ27: How do you build multi-architecture images?
Section titled “Q27: How do you build multi-architecture images?”Answer:
# Create and use a multi-arch builderdocker buildx create --name multiarch --use
# Build for multiple platformsdocker buildx build \ --platform linux/amd64,linux/arm64,linux/arm/v7 \ -t username/myapp:latest \ --push \ .
# Inspect manifestdocker buildx imagetools inspect username/myapp:latestQ28: What is Docker content trust?
Section titled “Q28: What is Docker content trust?”Answer: Docker Content Trust (DCT) ensures image integrity via digital signatures.
# Enable DCTexport DOCKER_CONTENT_TRUST=1
# Sign and push image (requires notary key)docker trust key generate my-keydocker trust sign username/myapp:1.0
# Inspect trust datadocker trust inspect username/myapp:1.0
# Verify pull (only signed images allowed)docker pull username/myapp:1.0Q29: How do you handle Docker in production?
Section titled “Q29: How do you handle Docker in production?”Answer: Production Docker checklist:
# Complete production docker run commanddocker run \ --name myapp \ --detach \ # --- Image --- myapp:1.5.3 \ # Always pin version, never :latest # --- Restart --- --restart unless-stopped \ # Auto-restart on failure (not always — could hide bugs) # --- Resources --- --memory=512m \ # Memory limit (OOM kill if exceeded) --memory-swap=512m \ # No swap (disk-based = slow) --cpus=1.0 \ # CPU limit --pids-limit=100 \ # Limit processes (prevent fork bombs) # --- Security --- --user 1001:1001 \ # Non-root user --read-only \ # Immutable root filesystem --tmpfs /tmp:rw,noexec,nosuid \ # Writable temp, no execution --cap-drop ALL \ # Drop all capabilities --cap-add NET_BIND_SERVICE \ # Add only what's needed --security-opt no-new-privileges:true \ # --- Networking --- --network my-app-net \ # Isolated network -p 127.0.0.1:8080:3000 \ # Bind to localhost only # --- Logging --- --log-driver=json-file \ --log-opt max-size=10m \ --log-opt max-file=3 \ # --- Health --- --health-cmd='curl -f http://localhost:3000/health || exit 1' \ --health-interval=30s \ --health-timeout=5s \ --health-retries=3 \ --health-start-period=30sRestart policies explained:
# no — Never restart (useful for one-off jobs)# on-failure — Only restart if exit code != 0# always — Always restart (even on docker stop — restarts on daemon start)# unless-stopped — Restart unless manually stopped (most common for services)docker run --restart=unless-stopped myappInterview tip: Use
--restart=unless-stoppedfor services. Use--restart=on-failure:3for jobs that might fail (limits retries). Never use--restart=alwaysin production as it prevents intentional stops from sticking.
Q30: What happens when you run docker run?
Section titled “Q30: What happens when you run docker run?”Answer: Step-by-step execution:
docker run -d -p 8080:80 --name my-nginx nginx:alpine
Step 1: Docker CLI parses command, sends to daemon → POST /containers/create to Docker daemon (Unix socket: /var/run/docker.sock)
Step 2: Daemon checks if nginx:alpine exists locally → Not found → pulls from Docker Hub (registry.hub.docker.com) → Downloads each layer
Step 3: Daemon creates container object → Assigns container ID (e.g., a1b2c3d4e5f6...)
Step 4: Creates Linux namespaces → PID namespace (container has its own PID 1 = nginx) → NET namespace (container gets own network stack) → MNT namespace (container has isolated filesystem view) → UTS namespace (container has own hostname) → IPC namespace
Step 5: Applies cgroups (resource limits) → CPU, memory limits configured
Step 6: Sets up networking → Creates veth pair (virtual ethernet) → Assigns IP from bridge network (172.17.0.x) → Sets up port forwarding: host:8080 → container:80
Step 7: Mounts filesystem → Stacks image layers (overlayfs) → Creates read-write layer on top → Mounts volumes/bind mounts
Step 8: Starts process → Runs ENTRYPOINT/CMD as PID 1 inside container → Container is now 'running'# See the daemon API callsdocker run --rm -it nginx:alpine &# Docker used POST /containers/create then POST /containers/{id}/start
# Check what PID nginx has on HOST (different from container's PID 1)docker inspect my-nginx --format='{{.State.Pid}}'# → 12345 (host PID)pgrep nginx # Same 12345 appears here tooInterview tip: “Every container is just a process on the host with isolated namespaces and limited resources via cgroups. There’s no magic —
docker runcreates namespaces, sets up networking, and exec()s your process.”
Section 9: Docker Interview Quick-Fire
Section titled “Section 9: Docker Interview Quick-Fire”Q31: What is the difference between COPY and ADD in Dockerfile?
Section titled “Q31: What is the difference between COPY and ADD in Dockerfile?”Answer:
| Feature | COPY | ADD |
|---|---|---|
| Copy local files | ✅ Yes | ✅ Yes |
| Copy from URL | ❌ No | ✅ Yes |
| Extract tar archives | ❌ No | ✅ Yes (auto-extracts) |
| Recommended? | ✅ Preferred | Only when needed |
# COPY — simple and predictable (recommended)COPY ./app /appCOPY package*.json /app/
# ADD — use only for these specific casesADD https://example.com/file.tar.gz /tmp/ # Download from URLADD archive.tar.gz /app/ # Auto-extract tarInterview tip: “Use COPY unless you need URL downloading or automatic tar extraction — COPY is more transparent and predictable.”
Q32: What is the difference between RUN, CMD, and ENTRYPOINT?
Section titled “Q32: What is the difference between RUN, CMD, and ENTRYPOINT?”Answer:
| Instruction | When runs | Override at runtime | Purpose |
|---|---|---|---|
RUN | During docker build | N/A | Install software, build steps |
CMD | When container starts | docker run image <cmd> | Default arguments |
ENTRYPOINT | When container starts | --entrypoint flag | Fixed executable |
# RUN — runs during build (creates layer)RUN apt-get install -y curl
# CMD — default command, easily overrideableCMD ["node", "server.js"]# docker run myimage python app.py ← overrides CMD
# ENTRYPOINT — fixed executableENTRYPOINT ["nginx"]CMD ["-g", "daemon off;"]# docker run myimage -c /etc/nginx/nginx.conf ← CMD becomes arg to ENTRYPOINTQ33: How does Docker layer caching work?
Section titled “Q33: How does Docker layer caching work?”Answer: Docker caches each layer. Once a layer changes, ALL subsequent layers are invalidated.
# BAD - npm install runs every time ANY file changesCOPY . .RUN npm install
# GOOD - npm install only runs when package.json changesCOPY package*.json ./RUN npm install # Cached unless package.json changesCOPY . . # Only this layer re-runs when code changesLayer cache hit: ✅ Uses cached layer (fast)Layer cache miss: ❌ Rebuilds this + all subsequent layers (slow)
COPY package.json → RUN npm install → COPY . → RUN build ↑ ↑ Rarely changes Changes often (cache hit ✅) (cache miss ❌ but only this layer)Q34: What is Docker namespace and cgroups?
Section titled “Q34: What is Docker namespace and cgroups?”Answer: Docker uses Linux kernel features for isolation:
- Namespaces → What a process can see (isolation)
- cgroups → What a process can use (resource limits)
# Namespaces Docker uses:# - PID namespace: container has its own PID 1# - NET namespace: container has its own network stack# - MNT namespace: container has its own filesystem view# - UTS namespace: container has its own hostname# - IPC namespace: container has its own IPC resources# - USER namespace: container can have its own user IDs
# Verify: Container PID 1 is different from hostdocker run -d nginxdocker exec -it nginx_container ps aux # Shows PID 1 inside containerps aux | grep nginx # Different PID on host
# cgroups — resource limitsdocker run --memory=512m --cpus=1.0 nginx# Check cgroup limitscat /sys/fs/cgroup/memory/docker/<container_id>/memory.limit_in_bytesQ35: How do you minimize Docker image size?
Section titled “Q35: How do you minimize Docker image size?”Answer:
# 1. Use minimal base imagesFROM scratch # Empty image (for static binaries)FROM alpine:3.18 # ~5MBFROM distroless/nodejs # ~30MB, no shell (secure)FROM node:18-alpine # ~170MB
# 2. Multi-stage buildsFROM node:18 AS builderWORKDIR /appCOPY package*.json ./RUN npm ciCOPY . .RUN npm run build
FROM node:18-alpine AS runtimeCOPY --from=builder /app/dist ./distCOPY --from=builder /app/node_modules ./node_modulesCMD ["node", "dist/index.js"]# Result: 170MB instead of 1GB
# 3. Combine RUN commands# BAD: 3 layersRUN apt-get updateRUN apt-get install -y curlRUN rm -rf /var/lib/apt/lists/*
# GOOD: 1 layerRUN apt-get update && \ apt-get install -y curl && \ rm -rf /var/lib/apt/lists/*
# 4. Use .dockerignore# .dockerignorenode_modules/.git/*.log.envdist/coverage/Q36: What is Docker Compose health check dependency?
Section titled “Q36: What is Docker Compose health check dependency?”Answer:
# Problem: app starts before db is ready → crashservices: app: depends_on: - db # Only waits for container START, not readiness
# Solution: Use condition: service_healthyservices: app: image: myapp:1.0 depends_on: db: condition: service_healthy # Waits for db to be healthy restart: unless-stopped
db: image: postgres:15 environment: POSTGRES_PASSWORD: secret healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 3s retries: 5 start_period: 10sQ37: What is a Docker overlay network?
Section titled “Q37: What is a Docker overlay network?”Answer: Overlay networks allow containers on different Docker hosts to communicate — used in Docker Swarm.
# Create overlay network (requires Swarm mode)docker swarm initdocker network create \ --driver overlay \ --attachable \ my-overlay-net
# Deploy service on overlay networkdocker service create \ --name web \ --network my-overlay-net \ --replicas 3 \ nginx
# Containers on different nodes can talk via overlay# Container on Node1 → Container on Node2 via my-overlay-netQ38: How do you copy files between container and host?
Section titled “Q38: How do you copy files between container and host?”Answer:
# Host → Containerdocker cp ./config.json my-container:/app/config.jsondocker cp ./ssl-certs/ my-container:/etc/ssl/
# Container → Hostdocker cp my-container:/app/logs/error.log ./local-error.logdocker cp my-container:/var/log/ ./container-logs/
# Common use case: Backup data from containerdocker cp postgres-db:/var/lib/postgresql/data/ ./postgres-backup/Q39: What is the difference between docker stop and docker kill?
Section titled “Q39: What is the difference between docker stop and docker kill?”Answer:
| Command | Signal | Behavior |
|---|---|---|
docker stop | SIGTERM → SIGKILL (after timeout) | Graceful shutdown |
docker kill | SIGKILL (immediately) | Force kill |
# docker stop — sends SIGTERM, waits 10s, then SIGKILLdocker stop my-container # Default 10s grace perioddocker stop --time=30 my-container # 30s grace period
# docker kill — immediate SIGKILLdocker kill my-containerdocker kill --signal=SIGTERM my-container # Can specify signal
# Best practice: Use 'docker stop' in production# App should handle SIGTERM for graceful cleanup:# - Close DB connections# - Finish in-flight requests# - Flush buffersQ40: How do you pass environment variables to Docker containers?
Section titled “Q40: How do you pass environment variables to Docker containers?”Answer:
# 1. Inline at runtimedocker run -e DB_HOST=localhost -e DB_PORT=5432 myapp
# 2. From host environment variabledocker run -e DB_PASSWORD myapp # Passes $DB_PASSWORD from host
# 3. From .env filedocker run --env-file .env myapp
# 4. In docker-compose.ymlservices: app: environment: - DB_HOST=postgres - NODE_ENV=production - DB_PASSWORD=${DB_PASSWORD} # From host .env env_file: - .env.production # File-based# Check what environment a container hasdocker exec my-container envdocker inspect my-container | grep -A20 EnvQ41: What are Docker container exit codes?
Section titled “Q41: What are Docker container exit codes?”Answer:
| Exit Code | Meaning | Common Cause |
|---|---|---|
0 | Success | Normal exit |
1 | Application error | App crash, unhandled exception |
125 | Docker daemon error | Invalid docker command |
126 | Permission denied | CMD not executable |
127 | Command not found | Wrong CMD path |
137 | SIGKILL | OOM killed, docker kill |
139 | Segfault | Application segmentation fault |
143 | SIGTERM | Graceful stop (docker stop) |
# Check exit codedocker inspect container_name --format='{{.State.ExitCode}}'
# Container OOM killed?docker inspect container_name | grep OOMKilled# "OOMKilled": true → increase memory limitQ42: What is Docker socket and why is it dangerous to mount?
Section titled “Q42: What is Docker socket and why is it dangerous to mount?”Answer: The Docker socket (/var/run/docker.sock) is how Docker CLI talks to the Docker daemon.
# Mounting Docker socket gives container FULL control over host Dockerdocker run -v /var/run/docker.sock:/var/run/docker.sock myapp# This container can now:# - Create containers on the host# - Stop/delete any container# - Escape to host by running privileged container# = Essentially root on the host!
# Safer alternative: Use Docker-in-Docker (DinD) for CI# Or use Kaniko for building images without Docker socketdocker run \ gcr.io/kaniko-project/executor:latest \ --context=dir:///workspace \ --destination=myapp:latestQ43: How do you implement Docker image tagging strategy?
Section titled “Q43: How do you implement Docker image tagging strategy?”Answer:
# Strategy: semantic tags + commit SHA + latest
# During CI buildIMAGE=myorg/myappGIT_SHA=$(git rev-parse --short HEAD)VERSION=v1.5.0
# Build and tag multiple waysdocker build -t $IMAGE:$GIT_SHA .docker tag $IMAGE:$GIT_SHA $IMAGE:$VERSIONdocker tag $IMAGE:$GIT_SHA $IMAGE:latest
# Push all tagsdocker push $IMAGE:$GIT_SHA # Immutable, always points to this builddocker push $IMAGE:$VERSION # Semantic versiondocker push $IMAGE:latest # Latest release
# For GitFlow branchesdocker tag $IMAGE:$GIT_SHA $IMAGE:main-latest # Main branch latestdocker tag $IMAGE:$GIT_SHA $IMAGE:develop-latest # Dev branch latestQ44: What is a multi-stage build and when should you use it?
Section titled “Q44: What is a multi-stage build and when should you use it?”Answer: Multi-stage builds use multiple FROM statements to separate build and runtime environments.
# Go application — classic multi-stage# Stage 1: Build (uses full Go environment)FROM golang:1.21 AS builderWORKDIR /appCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 GOOS=linux go build -o server .
# Stage 2: Runtime (minimal, just the binary)FROM scratch AS runtime # Empty image!COPY --from=builder /app/server /serverCOPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/EXPOSE 8080ENTRYPOINT ["/server"]# Final image: ~15MB vs 800MB with full Go image# Build specific stagedocker build --target builder -t myapp:build .docker build --target runtime -t myapp:prod .When to use:
- Compiled languages (Go, Java, Rust, C++)
- Frontend apps (build with Node, serve with nginx)
- Whenever build tools shouldn’t be in production
Q45: How do you handle Docker resource cleanup?
Section titled “Q45: How do you handle Docker resource cleanup?”Answer:
# Remove stopped containersdocker container prune
# Remove unused imagesdocker image prune # Only dangling imagesdocker image prune -a # All unused images
# Remove unused volumesdocker volume prune
# Remove unused networksdocker network prune
# Nuclear option — remove everything unuseddocker system prune -a --volumes# WARNING: This removes ALL unused images, containers, networks, volumes
# Check disk usagedocker system dfdocker system df -v # Verbose breakdown
# Automate cleanup in CIdocker rmi $(docker images -f "dangling=true" -q)Q46: What is Docker Swarm and how does it differ from Kubernetes?
Section titled “Q46: What is Docker Swarm and how does it differ from Kubernetes?”Answer:
| Aspect | Docker Swarm | Kubernetes |
|---|---|---|
| Setup | docker swarm init (1 command) | Complex (kubeadm, eksctl) |
| Scaling | docker service scale | HPA + manual |
| Load balancing | Built-in (VIP) | Requires Ingress controller |
| Rolling updates | Built-in | Built-in |
| Config management | Docker secrets/configs | ConfigMap/Secrets |
| Storage | Volumes | PV/PVC/StorageClass |
| Networking | Overlay | CNI plugins |
| Learning curve | Low | High |
| Production use | Small teams | Enterprise |
# Swarm setupdocker swarm init --advertise-addr 192.168.1.10
# Deploy stack (Compose file on Swarm)docker stack deploy -c docker-compose.yml myapp
# Scale servicedocker service scale myapp_web=5
# View servicesdocker service lsdocker service ps myapp_webQ47: How do you implement Docker image vulnerability scanning in CI/CD?
Section titled “Q47: How do you implement Docker image vulnerability scanning in CI/CD?”Answer:
# GitHub Actions — scan before pushjobs: build-and-scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Build image run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy scan uses: aquasecurity/trivy-action@master with: image-ref: myapp:${{ github.sha }} format: table exit-code: '1' # Fail pipeline if vulnerabilities found ignore-unfixed: true # Ignore vulns with no fix severity: CRITICAL,HIGH # Only fail on critical/high
- name: Push (only if scan passes) if: success() run: | docker tag myapp:${{ github.sha }} myorg/myapp:latest docker push myorg/myapp:latestQ48: What is Docker’s Union File System?
Section titled “Q48: What is Docker’s Union File System?”Answer: Docker uses a Union File System (overlayfs) to stack image layers on top of each other.
Image layers (read-only): Layer 4: COPY app/ /app → /app/server.js Layer 3: RUN npm install → /app/node_modules/ Layer 2: COPY package.json → /app/package.json Layer 1: FROM node:18 → /usr/bin/node, /lib/, etc. ↓Container runtime adds: Layer 5: Read-Write layer → Any file changes happen here
When you delete a file in a container: A "whiteout" file is created in the RW layer It masks the file from lower layers (doesn't actually delete from image)# Inspect image layersdocker history myapp:1.0docker inspect myapp:1.0 | jq '.[0].RootFS'
# View overlayfs mountsdocker inspect container_name | jq '.[0].GraphDriver'Q49: How do you run Docker containers in production securely?
Section titled “Q49: How do you run Docker containers in production securely?”Answer:
# Secure docker run command with all best practicesdocker run \ --name myapp \ --user 1001:1001 \ # Non-root user --read-only \ # Read-only root filesystem --tmpfs /tmp:rw,noexec,nosuid \ # Writable temp dir, no exec --cap-drop ALL \ # Drop all Linux capabilities --cap-add NET_BIND_SERVICE \ # Add only what's needed --security-opt no-new-privileges:true \ --security-opt seccomp=default \ --memory=512m \ # Memory limit --memory-swap=512m \ # No swap --cpus=1.0 \ # CPU limit --pids-limit=100 \ # Limit process spawning --restart=unless-stopped \ # Auto-restart policy --log-driver=json-file \ --log-opt max-size=10m \ --log-opt max-file=3 \ --health-cmd="curl -f http://localhost:8080/health" \ --health-interval=30s \ --network my-private-net \ --env-file /run/secrets/myapp.env \ myapp:1.5.0 # Pinned version, not :latestQ50: What is the difference between Docker volumes and bind mounts in practice?
Section titled “Q50: What is the difference between Docker volumes and bind mounts in practice?”Answer:
| Feature | Named Volume | Bind Mount |
|---|---|---|
| Managed by | Docker | Host OS |
| Path | /var/lib/docker/volumes/ | Any host path |
| Portable | ✅ Yes | ❌ Host-dependent |
| Backup/restore | docker volume commands | Regular file ops |
| Performance | Optimized (Linux) | Slightly slower |
| Best for | Production data | Local development |
# Named volume (production)docker run -v myapp-data:/app/data postgres:15# Bind mount (development - live reload)docker run -v $(pwd)/src:/app/src -v $(pwd)/public:/app/public myapp# Changes to ./src immediately reflected in container
# Read-only bind mount (config files)docker run -v /host/config.json:/app/config.json:ro myapp
# Backup named volumedocker run --rm \ -v myapp-data:/source:ro \ -v $(pwd)/backup:/backup \ alpine tar czf /backup/data-$(date +%Y%m%d).tar.gz /sourceBack to DevOps Q&A Index