Docker from Basics to Production · Capstone

Capstone: Containerize and Ship a Three-Service App

Take a web frontend, an API and a background worker with PostgreSQL and Redis from 'works on my machine' to images in a registry and a production-like Compose setup.

Create a free account to save this page, take notes and track your progress. Sign in

Scenario

Example company: a startup's product has a React frontend (built to static files), a Python API, a Python background worker, PostgreSQL and Redis. Developers install everything locally with different versions; the production server is set up by hand. They want containers for development, CI and a first production deployment on a single VM with Compose.

Requirements

  • One command starts the full stack for development, with live reload for the API.
  • CI builds images on every merge, tests them, and pushes them with immutable tags.
  • The production VM runs the exact images CI tested, restarts crashed services, and keeps database data safe.

Constraints

  • The team has no Kubernetes experience; production is one VM for now.
  • Secrets (database password, API keys) must never be in Git or images.
  • The API must not drop requests during deploys.

Deliverables

  1. Dockerfiles for frontend, API and worker (stages, base images, users).
  2. Compose files for development and production (what differs and how).
  3. CI pipeline steps and tagging.
  4. Secrets and configuration handling.
  5. Deploys, restarts and data safety on the VM.

Hints

  • The frontend's runtime is just static files.
  • API and worker can share one image with different commands.
  • Healthchecks matter for startup order and for zero-downtime deploys.

Draft each deliverable below before revealing the model answer and rubric.

Further reading: Dockerfile best practices · Compose in production

Deliverables

Describe the Dockerfiles.

Model answer

  • Frontend: stage 1 node:24-alpine runs npm ci and npm run build; stage 2 nginx:1.29-alpine (or an unprivileged nginx variant) serves /usr/share/nginx/html with a small config for SPA routing and caching headers.
  • API and worker: one image from python:3.14-slim: dependency layer from a lockfile, then code, non-root user, PYTHONUNBUFFERED=1; CMD starts the API (exec form, gunicorn or uvicorn), and the worker service overrides the command.
  • .dockerignore excludes .git, node_modules, .env, tests artefacts; base images pinned and rebuilt weekly.

Rubric

Look for multi-stage frontend with a static server, a shared Python image with different commands, non-root users, pinned bases and .dockerignore.

Describe the Compose setup for development and production.

Model answer

  • compose.yaml (production-like): services web, api, worker, db, cache; images by immutable tag; healthchecks on db, cache and api; depends_on with service_healthy; named volumes for PostgreSQL; restart: unless-stopped; only web publishes a port (or a reverse proxy does).
  • compose.override.yaml (development, loaded automatically): build: instead of image:, bind-mounted source with auto-reload, debug ports on 127.0.0.1, watch rules.
  • Production runs docker compose -f compose.yaml up -d so the override is never used there.

Rubric

Strong answers separate production-like base from development overrides, use healthchecks and named volumes, and limit published ports.

Describe the CI pipeline.

Model answer

  • On every merge: build each image with Buildx and registry or CI cache; tag with the Git SHA.
  • Run unit tests inside the API image and an integration test with docker compose up --wait and a smoke test.
  • Scan images; fail on critical vulnerabilities with available fixes.
  • Push SHA tags; on release, add version tags; record digests in the deployment manifest (the production compose file references digests or SHA tags).

Rubric

Look for build once, test the shipped image, scan, immutable tags and recorded digests.

How are secrets and configuration handled?

Model answer

  • Non-secret configuration via environment variables in the compose files.
  • Secrets as Compose secrets files on the VM (created from a secret manager by the deploy script, permissions 600), read from /run/secrets by the applications; .env files are git-ignored.
  • Build-time credentials (private package registry) through BuildKit secret mounts in CI.

Rubric

Strong answers keep secrets out of Git, images and build args, and deliver them as files at run time.

How do deploys, restarts and data safety work on the VM?

Model answer

  • Deploy: pull the new tags, docker compose up -d service by service; for the API, run a second replica behind the reverse proxy and switch only after its healthcheck passes (or use a proxy that drains connections), and handle SIGTERM gracefully.
  • restart: unless-stopped on all services; log rotation configured in daemon.json.
  • PostgreSQL data on a named volume with nightly pg_dump to off-VM storage and tested restores; never down -v in production.
  • Migrations run as a one-off docker compose run --rm api migrate before switching traffic.

Rubric

Look for health-gated deploys with graceful shutdown, restart policies, log rotation, backups with restore tests and controlled migrations.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?