Q: Write an optimized, secure multi-stage Dockerfile for a production web service during screen sharing.
How to write a production-ready, highly secure multi-stage Dockerfile during a live screen-sharing interview: separate dependency caching, compilation, unprivileged non-root user execution, and minimal runtime footprint.
#Docker #Dockerfile #Multi-Stage #Security #Non-Root #Node.js #Best Practices
🎙️ Candidate Opening & Architectural Context
"When asked to write a Dockerfile in a live coding interview, I structure it using multi-stage builds to keep the final image minimal, secure, and reproducible. I isolate dependency caching, keep compilers and package managers out of the runtime image, and run the container as an unprivileged non-root user with pinned alpine or distroless base images."
Advertisement
🛠️ Production Runbook & Step-by-Step Resolution
1️⃣
Production-Grade Multi-Stage Dockerfile Implementation
Walk the interviewer through each stage: dependency resolution, build stage, and hardened unprivileged runtime:
# syntax=docker/dockerfile:1
# Stage 1: Install dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
# Stage 2: Build application
FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build && npm prune --omit=dev
# Stage 3: Minimal production runtime
FROM node:20-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/package*.json ./
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
# Security: Run as unprivileged non-root user
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]
- Stage 1 (deps): Copies package manifests and runs
npm cito leverage Docker layer caching when source code changes. - Stage 2 (build): Compiles TypeScript/assets and strips development dependencies with
npm prune --omit=dev. - Stage 3 (runtime): Uses a lean base image, copies only the compiled output and production modules, sets
NODE_ENV=production, and runs as non-root usernode.
2️⃣
Building, Verification & Security Best Practices
Demonstrate how you verify and test the built container image during the interview:
# Build and tag the multi-stage image
docker build -t sample-api:multi .
# Verify image size reduction
docker images | grep sample-api
# Run and verify unprivileged execution
docker run --rm -p 3000:3000 sample-api:multi
# Verify running user is non-root
docker run --rm sample-api:multi whoami
- Image Size Comparison: Compare the multi-stage image (~80MB) against a single-stage image (>1GB).
- Non-Root Verification: Execute
idinside the container to prove it runs as UID 1000 rather than root. - Security Hardening: Highlight the inclusion of a
.dockerignorefile to prevent leaking.git, localnode_modules, and secrets into the image build context.
💡 The Senior SRE Gold Nugget (Key Architectural Takeaway)
"In live coding interviews, articulate why each layer exists: cache package manifests first to speed up rebuilds, prune devDependencies before the final stage, copy only compiled artifacts, and always enforce a non-root USER."
⚡ 60-Second Elevator Pitch Talking Points
- Structure the Dockerfile into distinct stages: dependency caching, building/pruning, and minimal unprivileged runtime.
- Leverage layer caching by copying package manifests before application code to avoid re-downloading modules on every commit.
- Harden security by running as a non-root user (USER node / UID 10001) and excluding build tools, test files, and package caches from the final image.
Advertisement