Docker Interview Questions & Answers Part 02
40+ Docker interview questions and answers covering architecture, Dockerfile, networking, security, and troubleshooting — Basic to Advanced.
Ans:
Docker is a platform for building, shipping, and running applications inside containers — lightweight, isolated environments that package the application with all its dependencies.
Why Docker?
- Consistency — “Works on my machine” problem solved. Same container runs everywhere.
- Isolation — Each container has its own filesystem, network, and process space
- Portability — Run on any OS that has Docker installed
- Efficiency — Containers share the host OS kernel (unlike VMs with full OS copies)
- Speed — Containers start in milliseconds
# Run a container
docker run -d -p 80:80 --name webserver nginx
# Check running containers
docker ps
# Check all containers (including stopped)
docker ps -a
Ans:
| Feature | Docker Container | Virtual Machine |
|---|---|---|
| OS | Shares host OS kernel | Full guest OS |
| Size | MBs | GBs |
| Startup | Milliseconds | Minutes |
| Isolation | Process-level | Hardware-level |
| Portability | High | Lower |
| Performance | Near-native | Overhead from hypervisor |
| Use case | Microservices, apps | Full OS environments |
VM Architecture: Docker Architecture:
┌──────────────────┐ ┌──────────────────┐
│ App A │ App B │ │ App A │ App B │
│ Libs │ Libs │ │ Libs │ Libs │
│ Guest OS│ Guest OS│ │──────────────────│
│──────────────────│ │ Docker Engine │
│ Hypervisor │ │──────────────────│
│─────────────────-│ │ Host OS │
│ Host OS │ │──────────────────│
│ Hardware │ │ Hardware │
└──────────────────┘ └──────────────────┘
Ans:
Docker uses a client-server architecture:
Docker CLI (client)
│ REST API
▼
Docker Daemon (dockerd) ← server running on host
│
├── Images
├── Containers
├── Networks
└── Volumes
Key components:
| Component | Role |
|---|---|
Docker Client (docker CLI) | Sends commands to Docker daemon |
Docker Daemon (dockerd) | Manages containers, images, networks, volumes |
| Docker Images | Read-only templates used to create containers |
| Docker Containers | Running instances of images |
| Docker Registry | Stores and distributes images (Docker Hub, ECR, etc.) |
| Docker Networks | Isolated networking for containers |
| Docker Volumes | Persistent storage for containers |
# Client sends command to daemon
docker run nginx
# Docker client → API call → dockerd → pulls nginx image → creates container
Ans:
A Dockerfile is a text file containing instructions to build a Docker image. It automates the image creation process.
Why use Dockerfile:
- Reproducible builds — same Dockerfile = same image every time
- Version-controlled — stored in Git alongside code
- Automation — builds are scripted, not manual
# Basic Dockerfile example
FROM node:18-alpine # Base image
WORKDIR /app # Set working directory
COPY package*.json ./ # Copy dependency files
RUN npm install # Install dependencies
COPY . . # Copy application code
EXPOSE 3000 # Document port
CMD ["node", "server.js"] # Default command to run
# Build image from Dockerfile
docker build -t myapp:1.0 .
# Build with no cache
docker build --no-cache -t myapp:1.0 .
Ans:
| Instruction | Purpose |
|---|---|
FROM | Base image to build on |
RUN | Execute commands during build (creates a layer) |
CMD | Default command when container starts (can be overridden) |
ENTRYPOINT | Main process; CMD becomes its arguments |
COPY | Copy files from host to image |
ADD | Like COPY, but also extracts tarballs and supports URLs |
EXPOSE | Documents which port the app listens on |
ENV | Set environment variables |
ARG | Build-time variables |
WORKDIR | Set working directory |
USER | Set user to run as |
VOLUME | Create a mount point |
LABEL | Add metadata |
HEALTHCHECK | Define health check command |
FROM ubuntu:22.04
LABEL maintainer="dev@example.com"
ARG APP_VERSION=1.0
ENV APP_ENV=production
WORKDIR /app
COPY . .
RUN apt-get update && apt-get install -y python3
EXPOSE 8080
USER appuser
HEALTHCHECK --interval=30s CMD curl -f http://localhost:8080/health
ENTRYPOINT ["python3"]
CMD ["app.py"]
Ans:
| Instruction | When it runs | Purpose | Overridable? |
|---|---|---|---|
RUN | Build time | Execute commands to build the image (install packages, etc.) | N/A |
CMD | Runtime | Default arguments/command when container starts | Yes (docker run img custom-cmd) |
ENTRYPOINT | Runtime | Fixed main process of the container | Only with --entrypoint flag |
Practical example:
FROM nginx
RUN apt-get update && apt-get install -y curl # Build time only
ENTRYPOINT ["nginx"] # Always runs nginx
CMD ["-g", "daemon off;"] # Default args (can be overridden)
# CMD can be overridden:
docker run myimage echo "hello" # Overrides CMD
# ENTRYPOINT stays, CMD becomes args:
docker run myimage -c /etc/nginx/nginx.conf # "-c ..." overrides CMD only
Ans:
| Network | Description | Use Case |
|---|---|---|
| bridge | Default. Containers get private IPs, communicate via bridge interface | Same-host container communication |
| host | Container uses host’s network namespace directly | High performance, no port mapping needed |
| none | No network interface. Complete isolation | Security-sensitive containers |
| overlay | Multi-host networking (Docker Swarm/K8s) | Distributed apps across nodes |
| macvlan | Container gets its own MAC/IP on physical network | Legacy apps expecting direct network access |
# List networks
docker network ls
# Create custom bridge network
docker network create --driver bridge mynetwork
# Run container on custom network
docker run -d --network mynetwork --name app nginx
# Inspect network
docker network inspect mynetwork
# On same network, containers can resolve each other by name:
docker run -it --network mynetwork alpine ping app
Ans:
# Step 1: Find the running container ID
docker ps
# Step 2: Run the container and install packages
docker exec -it <container_id> bash
apt-get install -y curl vim
# Step 3: Commit the container to a new image
docker commit <container_id> myimage:v2
# With author and message:
docker commit \
--author "Dev Team" \
--message "Added curl and vim" \
<container_id> myimage:v2
# Verify the new image
docker images | grep myimage
docker run myimage:v2 which curl # Test curl is available
# Step 4 (Best Practice): Update your Dockerfile instead!
# RUN apt-get install -y curl vim
# docker build -t myimage:v2 .
Note:
docker commitis useful for quick fixes but should always be followed by updating the Dockerfile for reproducibility.
Ans:
# Step 1: Check restart count and status
docker ps -a
# Look at RESTARTS column and STATUS
# Step 2: Check container logs (recent entries)
docker logs <container_name>
# Step 3: Check logs with timestamps
docker logs --timestamps <container_name>
# Step 4: Follow logs in real-time during restart
docker logs -f <container_name>
# Step 5: Check exit code
docker inspect <container_name> \
--format '{{.State.ExitCode}} {{.State.Error}}'
# Exit 1 = app error, 137 = OOMKilled, 139 = segfault, 143 = SIGTERM
# Step 6: Check events
docker events --filter container=<container_name>
# Step 7: Check resource constraints
docker stats <container_name>
# Step 8: Check inside the container
docker exec -it <container_name> sh
Common causes:
- Application crash (check exit code 1 → app logs)
- OOMKilled (exit code 137 → increase memory limit)
- Port already in use
- Missing environment variables or config
Ans:
# Tip 1: Use slim/alpine base images
FROM node:18-alpine # 180MB vs FROM node:18 → 1.1GB
# Tip 2: Multi-stage builds (build in one stage, copy only artifacts)
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
# Final image only contains built files + nginx, no node_modules
# Tip 3: Combine RUN commands (each RUN = one layer)
# BAD (3 layers):
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# GOOD (1 layer):
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*
# Tip 4: Use .dockerignore
echo "node_modules\n.git\n*.log\ndist" > .dockerignore
# Tip 5: Don't copy unnecessary files
COPY src/ ./src/ # Instead of COPY . .
# Check image size
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
# Analyze layers
docker history myimage:1.0
Ans:
# Method 1: Docker Scout (built into Docker CLI)
docker scout cves myimage:1.0
docker scout recommendations myimage:1.0
# Method 2: Trivy (most popular, free, open-source)
# Install
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# Scan image
trivy image myimage:1.0
# Scan with specific severity
trivy image --severity HIGH,CRITICAL myimage:1.0
# Output as JSON for CI/CD
trivy image --format json -o results.json myimage:1.0
# Method 3: Snyk
snyk container test myimage:1.0
# Method 4: AWS ECR Scan (if using ECR)
aws ecr start-image-scan \
--repository-name my-repo \
--image-id imageTag=1.0
aws ecr describe-image-scan-findings \
--repository-name my-repo \
--image-id imageTag=1.0
# Integrate into CI/CD pipeline (Jenkins/GitHub Actions):
# Run trivy after docker build, fail pipeline if CRITICAL vulns found
trivy image --exit-code 1 --severity CRITICAL myimage:1.0
Ans:
# BAD (Never do this):
ENV DB_PASSWORD=mysecret123 # Visible in image layers
docker run -e DB_PASSWORD=mysecret # Visible in docker inspect
# Method 1: Docker Secrets (Docker Swarm)
echo "mysecret" | docker secret create db_password -
docker service create \
--secret db_password \
--name myapp myimage
# Secret available at: /run/secrets/db_password inside container
# Method 2: Environment variables from a file (for compose)
# .env file (not committed to git):
DB_PASSWORD=mysecret123
# docker-compose.yml:
# env_file:
# - .env
# Method 3: AWS Secrets Manager / SSM at runtime
# In entrypoint script:
export DB_PASSWORD=$(aws secretsmanager get-secret-value \
--secret-id prod/db-password \
--query SecretString --output text)
# Method 4: Vault (HashiCorp) Agent Sidecar
# Vault agent auto-renews and injects secrets into container
# Method 5: Kubernetes Secrets (when in K8s)
kubectl create secret generic db-secret --from-literal=password=mysecret
# Mount as volume or envFrom in pod spec
Ans:
# Immediate mitigation:
# Step 1: Confirm and assess the vulnerability
trivy image myimage:1.0 --severity CRITICAL
# Step 2: Check if the vulnerable package is actually used
docker run myimage:1.0 dpkg -l | grep <package-name>
# Step 3: If critical — isolate/rollback immediately
# Kubernetes rollback:
kubectl rollout undo deployment/myapp
# Docker Swarm rollback:
docker service rollback myapp-service
# Step 4: Fix the vulnerability
# Update base image:
# FROM node:18-alpine → FROM node:18.20-alpine (patched version)
# Update specific package:
RUN apt-get update && apt-get install -y --only-upgrade <vulnerable-package>
# Step 5: Rebuild and rescan
docker build -t myimage:1.1 .
trivy image --exit-code 1 --severity CRITICAL myimage:1.1
# Step 6: Secure the CI/CD pipeline going forward
# - Add trivy scan gate in Jenkins/GitHub Actions
# - Set up automated base image update PRs (Renovate/Dependabot)
# - Schedule weekly image rescans even for deployed images
Ans:
Containerization is the process of packaging an application and all its dependencies (code, runtime, libraries, config) into a single portable unit called a container.
How it works:
- Uses Linux kernel features: namespaces (isolation) and cgroups (resource limits)
- Containers share the host OS kernel — no Guest OS overhead
- Each container has its own: filesystem, network stack, process tree
# Namespaces used:
# PID namespace → isolated process IDs
# NET namespace → isolated networking
# MNT namespace → isolated filesystem
# UTS namespace → isolated hostname
# IPC namespace → isolated inter-process communication
# USER namespace → isolated user IDs
# cgroups control:
# CPU usage
# Memory limits
# I/O bandwidth
Benefits: Consistency, portability, density (many containers per host), fast startup.
Ans:
# Check running containers
docker ps
# Inspect container details (image, mounts, env)
docker inspect <container_name>
# Access container filesystem
docker exec -it <container_name> sh
ls -la /app/
cat /app/config.json
# Check mounted volumes
docker inspect <container_name> \
--format '{{json .Mounts}}' | python3 -m json.tool
# Copy file from container to host for inspection
docker cp <container_name>:/app/config.json /tmp/config.json
# Check image that the container is running
docker inspect <container_name> --format '{{.Config.Image}}'
# Check container logs for startup output
docker logs <container_name> --tail 50
Ans:
# Remove stopped containers
docker container prune -f
# Remove dangling images (untagged, not used)
docker image prune -f
# Remove all unused images (not just dangling)
docker image prune -a -f
# Remove unused volumes
docker volume prune -f
# Remove unused networks
docker network prune -f
# Nuclear option — remove everything unused
docker system prune -a -f --volumes
# Check disk usage before/after
docker system df
# In Jenkins pipeline — clean before build:
pipeline {
stages {
stage('Clean') {
steps {
sh 'docker system prune -f'
sh 'docker rmi myapp:old || true'
}
}
}
}
Ans:
| Command | Signal sent | Behavior |
|---|---|---|
docker stop | SIGTERM, then SIGKILL after timeout | Graceful shutdown. App can clean up. |
docker kill | SIGKILL by default | Immediate force kill. No cleanup. |
# Graceful stop (allows 10s for cleanup by default)
docker stop mycontainer
# Graceful stop with custom timeout
docker stop --time 30 mycontainer
# Force kill immediately
docker kill mycontainer
# Send specific signal
docker kill --signal SIGHUP mycontainer
Best practice: Always use docker stop first. Use docker kill only if the container is unresponsive.
Ans:
# Method 1: Check env vars inside running container
docker exec mycontainer env
docker exec mycontainer printenv DB_HOST
# Method 2: Inspect from outside
docker inspect mycontainer --format '{{json .Config.Env}}'
# Method 3: Validate at entrypoint script level
#!/bin/bash
# entrypoint.sh
required_vars=("DB_HOST" "DB_PORT" "APP_SECRET")
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
echo "ERROR: Required env var $var is not set"
exit 1
fi
done
exec "$@"
# Dockerfile:
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["node", "server.js"]
# Method 4: Docker Compose env validation
# Pass from .env file and validate with compose healthcheck
Ans:
# Method 1: Docker (standalone) — switch back to previous image tag
docker stop myapp
docker rm myapp
docker run -d --name myapp myimage:1.0 # Previous version
# Method 2: Docker Compose
# Revert docker-compose.yml to old image version
# image: myapp:1.0 (instead of 1.1)
docker-compose up -d
# Method 3: Docker Swarm
docker service rollback myapp-service
# Or update to previous image:
docker service update --image myimage:1.0 myapp-service
# Method 4: Kubernetes (if using K8s)
kubectl rollout undo deployment/myapp
kubectl rollout undo deployment/myapp --to-revision=3 # Specific revision
# Method 5: Scripted rollback
PREV_IMAGE=$(docker inspect myapp --format '{{.Config.Image}}')
# Store previous image tag in a file or environment variable before each deploy
docker stop myapp && docker rm myapp
docker run -d --name myapp $PREV_IMAGE
Best practice: Always tag images with version numbers, never just latest in production.
Ans:
A multi-stage build uses multiple FROM statements in a single Dockerfile to produce a minimal final image by copying only the necessary artifacts from build stages.
Use case: Compile/build in a fat image, ship only the binary in a slim image.
# Stage 1: Build (uses full Go/Node/Maven image)
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp ./cmd/
# Stage 2: Final image (tiny, secure)
FROM alpine:3.18
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/myapp .
EXPOSE 8080
CMD ["./myapp"]
# Build
docker build -t myapp:1.0 .
# Result: Final image is ~15MB instead of ~800MB
docker images myapp
Benefits:
- No build tools (gcc, npm, go) in production image
- Smaller attack surface
- Faster image pulls
Ans:
They’re not competitors — they operate at different layers and are almost always used together:
| Aspect | Docker | Kubernetes |
|---|---|---|
| What it is | Container runtime — builds and runs a single container | Container orchestrator — manages many containers across many machines |
| Scope | One host | A cluster of many hosts (nodes) |
| Handles | Building images, running/stopping containers | Scheduling, scaling, self-healing, networking, rolling updates across the whole fleet |
| Failure recovery | None built-in — if a container dies, it stays dead | Automatically restarts/reschedules failed containers (Pods) |
| Scaling | Manual (docker run more containers) | Declarative and automatic (Deployments, HPA) |
| Networking across hosts | Not handled | Built-in cluster networking (Services, DNS) |
Docker: "How do I package and run this one application?"
Kubernetes: "How do I run 200 containers across 50 servers,
keep them healthy, and route traffic to them?"
How they fit together:
Developer writes Dockerfile → docker build → image pushed to registry (ECR)
│
Kubernetes Deployment references that image ──────────┘
kubectl reads the Deployment → schedules Pods (running that image) across nodes
→ restarts them if they crash, scales them under load, load-balances traffic via Services
Note: Kubernetes no longer uses dockerd directly as its container runtime (deprecated via dockershim in 1.24+) — it now uses containerd or CRI-O directly. You can still build images with the docker CLI; Kubernetes just doesn’t depend on the Docker daemon to run them anymore.
Ans:
Here’s a multi-stage build for a React frontend, served by nginx — the ~1GB of build tooling never reaches the final image:
# Stage 1: Build — full Node toolchain, only exists during build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci # reproducible install from package-lock.json
COPY . .
RUN npm run build # outputs static files to /app/build
# Stage 2: Serve — tiny final image, no Node/npm at all
FROM nginx:1.25-alpine AS final
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
HEALTHCHECK --interval=30s CMD wget -q --spider http://localhost/ || exit 1
CMD ["nginx", "-g", "daemon off;"]
docker build -t frontend:1.0 .
docker images frontend
# builder stage: ~450MB (node_modules + build tools)
# final image: ~25MB (just nginx + static files)
Ans:
Without multi-stage builds, everything needed to build the app ends up baked into the same image you ship — compilers, package managers, source code, test files, dev dependencies — even though none of that is needed at runtime.
How multi-stage builds fix this:
Single-stage (bloated): Multi-stage (optimized):
┌─────────────────────┐ ┌──────────┐ ┌─────────────────┐
│ gcc, npm, go, git │ │ Build │ │ Final │
│ node_modules/ │ copy only │ Stage │ ───▶ │ - compiled binary│
│ source code │ final │ (fat, │ │ - or built assets│
│ compiled output │ artifact │ discarded)│ │ (slim, shipped) │
└─────────────────────┘ └──────────┘ └─────────────────┘
Mechanism: each FROM starts a brand-new, independent build stage. The COPY --from=<stage> instruction pulls only the specific files you name from an earlier stage into the current one — every layer from the build stage (package caches, compilers, intermediate files) is discarded because it’s never referenced by the final stage.
Why this matters beyond just disk space:
- Smaller attack surface — no compilers or package managers in production means fewer tools available to an attacker who gains shell access
- Faster deploys — smaller images pull and start faster, especially important for autoscaling and cold starts
- Faster CI — less data to push/pull from the registry on every build
This is different from just adding a .dockerignore or using apt-get remove after install (which still leaves traces in earlier layers) — multi-stage builds physically never copy the unwanted layers into the final image at all.
Ans:
A CVE is a publicly disclosed, uniquely-identified security vulnerability in software or a dependency, catalogued in a shared database maintained by MITRE and the NVD (National Vulnerability Database).
Anatomy of a CVE ID:
CVE-2024-3094
│ │ │
│ │ └── Sequence number for that year
│ └─────── Year the CVE was assigned/disclosed
└──────────── Fixed prefix
Why they matter for containers: a Docker image is a stack of layers — base OS packages, language runtime, and your app’s dependencies — and a CVE in any of them (even a transitive dependency you never directly installed) is a CVE in your image.
Severity is scored via CVSS (0–10):
| Score | Severity |
|---|---|
| 9.0–10.0 | Critical |
| 7.0–8.9 | High |
| 4.0–6.9 | Medium |
| 0.1–3.9 | Low |
A CVE existing doesn’t automatically mean you’re exploitable — whether the vulnerable code path is actually reachable in your app matters, which is why scanners increasingly try to flag reachable vulnerabilities, not just “package X version Y is present.”
Ans:
This is an experience question — interviewers want to see your remediation process, not memorized CVE numbers. A strong structure:
Example walkthrough: “Our Trivy scan in CI flagged CVE-2023-44487 (the ‘HTTP/2 Rapid Reset’ DoS vulnerability) as CRITICAL in a base image we were using.”
# 1. Confirm exposure — is the vulnerable component actually in use?
trivy image --severity CRITICAL myimage:1.0
docker run myimage:1.0 dpkg -l | grep <affected-package>
# 2. Check for an available patched version
trivy image myimage:1.0 --format json | jq '.Results[].Vulnerabilities[] | select(.VulnerabilityID=="CVE-2023-44487")'
# 3. Immediate mitigation while a fix is prepared (if internet-facing and unpatched)
# e.g., rate-limit at the load balancer / WAF layer
# 4. Permanent fix — bump the base image / package to the patched version
# FROM node:18.17-alpine → FROM node:18.18-alpine
# 5. Rebuild, rescan, verify clean
docker build -t myimage:1.1 .
trivy image --exit-code 1 --severity CRITICAL myimage:1.1
# 6. Roll out via normal pipeline (not a manual hotfix) so it's tracked and repeatable
Lesson to highlight: the recurring theme across most production CVEs isn’t zero-days — it’s stale base images. The fix that actually prevents recurrence is usually automated dependency update PRs (Renovate/Dependabot) plus a CI gate that fails the build on CRITICAL findings, not a one-off manual patch.
Ans:
| Tool | Type | Notes |
|---|---|---|
| Trivy | Image/filesystem/IaC scanner | Free, open-source, most widely used in CI pipelines |
| Grype | Image/filesystem scanner | Anchore’s open-source scanner, fast, SBOM-aware |
| Snyk | Dependency + container scanner | Commercial, strong IDE/PR integration, auto-fix PRs |
| Docker Scout | Image scanner | Built into the Docker CLI (docker scout cves) |
| AWS ECR Image Scanning | Registry-native scanner | Runs automatically on push if enabled on the repo |
| Clair | Static analysis scanner | Used by Quay and other registries |
| OWASP Dependency-Check | Dependency scanner | Focused on application-level libraries (Java, .NET, Node) |
| Dependabot / Renovate | Remediation (not detection) | Auto-opens PRs to bump vulnerable dependencies |
In a typical pipeline you’d pair a detection tool (Trivy/Grype scanning every build) with a remediation tool (Renovate/Dependabot opening the fix PRs automatically) so the loop from “CVE published” to “patched in production” doesn’t rely on someone manually noticing.
Ans:
This is meant as an experience question — interviewers want your reasoning, not just a name. A strong answer walks through the trade-off:
Example answer: “For a Node.js API, I used node:18-alpine as the runtime stage base image.”
Why alpine over the full/slim variants:
| Base image | Size | Notes |
|---|---|---|
node:18 | ~1.1 GB | Full Debian — includes build tools, docs, many packages you’ll never use |
node:18-slim | ~200 MB | Debian, stripped of most non-essential packages |
node:18-alpine | ~180 MB | musl libc + BusyBox instead of glibc — smallest option |
gcr.io/distroless/nodejs18 | ~150 MB | No shell, no package manager at all — max security, harder to debug |
Trade-offs I considered:
- Alpine’s musl libc occasionally causes subtle compatibility issues with native Node modules compiled against glibc — worth testing, not just assuming it “just works”
- Distroless is even smaller and more secure (no shell = smaller attack surface if compromised) but makes
kubectl execdebugging much harder since there’s no shell to exec into — I chose alpine as the practical middle ground since we still needed occasional interactive debugging in staging - For a build stage (not the final image), I used the full
node:18image since it needs the complete toolchain — multi-stage build discards it afterward, so its size doesn’t matter
What good answers avoid: picking latest (unpinned, unpredictable) or the largest/full image “because it’s safer” without weighing the size/attack-surface cost — the interviewer is checking whether you actively evaluated the trade-off rather than defaulting to whatever the tutorial used.
Add More Questions to This Guide
Know a question that should be here? Share it and help the community!
Open Google Form