Skip to content
advanced Phase 16 · Docker Capstone Project

Capstone: Build & Deploy

Implement Dockerfiles, Compose configs, and deploy the application.

3h
0 problems
Topic Progress 0%

Multi-Stage Dockerfiles

Multi-Stage Dockerfiles

One Dockerfile per service, each ending in a slim runtime stage:

FROM node:20-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-slim
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev
USER node
CMD ["node", "dist/server.js"]

Copy package*.json before the source so dependency installs cache until manifests change. Add a .dockerignore (node_modules, .git, local env files) to keep the context small and secrets out of layers. Never bake credentials into RUN — layers persist in image history.

Compose Configuration That Ships

Compose Configuration That Ships

services:
  api:
    build: ./api
    env_file: [.env]
    stop_grace_period: 30s
    healthcheck:
      test: ["CMD", "node", "-e", "fetch('http://localhost:3000/health').then(r=>{if(!r.ok)process.exit(1)})"]
      interval: 15s
      retries: 3
    deploy:
      resources:
        limits: { cpus: "1.0", memory: 512M }

env_file keeps config out of the repo; .env.example documents required keys. stop_grace_period must exceed your app's shutdown drain, or Compose kills in-flight requests. Validate before shipping with docker compose config, then bring it up with docker compose up -d --build.

Deploy and Verify

Deploy and Verify

Deploy is a loop you must observe, not a command you fire and forget:

docker compose up -d --build
docker compose ps              # health, not just "running"
docker compose logs -f api     # startup errors surface here
docker compose exec api node   # inspect the live process

Pitfalls: tagging everything latest, so a rollback pulls unknown bits — tag with the git SHA; secrets passed as build args, which leak into history — use runtime env or Docker secrets; ignoring SIGTERM, so every deploy drops connections — handle it and match stop_grace_period; and skipping CPU/memory limits, letting one bad service starve the host. In CI, run docker compose config to catch drift before it reaches a server.

Quiz

1. Why copy package.json before the rest of the source in a Dockerfile?

Question 1 options

2. What does stop_grace_period control?

Question 2 options

3. Which practice makes a rollback reproducible?

Question 3 options

Flashcards

Question

Multi-stage build

Answer

Compile in a builder stage, copy artifacts into a slim runtime stage — small images, no toolchain shipped.

Question

Layer-cache rule

Answer

Order Dockerfile steps from least- to most-frequently changing; COPY manifests before source.

Question

Graceful shutdown in Compose

Answer

Handle SIGTERM and set stop_grace_period longer than your drain time.

Question

docker compose config

Answer

Renders and validates the final config — run it in CI to catch YAML/env mistakes.

Revision Notes

Key Takeaways

  • 1. Multi-stage builds plus .dockerignore keep images small and secret-free
  • 2. Health checks, env_file, and resource limits make the stack production-shaped
  • 3. Deploy = up, then verify via ps/logs/exec before declaring success

Interview Tips

  • • Walk through your layer ordering and name the cache benefit of each COPY
  • • Explain the shutdown sequence: SIGTERM, drain, grace window, SIGKILL — and how you sized it

Cheat Sheet

Multi-stage + .dockerignore; manifests before source for cache; compose: env_file, healthcheck, stop_grace_period, limits. Deploy by SHA tag; verify ps/logs/exec; validate with compose config.

Continue Learning