Capstone โ Productionizing a Multi-Container App
๐ Capstone: Productionizing a Multi-Container App
The Application: A Task API + Database
A Node.js api service backed by a PostgreSQL db service โ small enough to stay readable here, real enough to exercise every chapter in this course.
Step 1: Multi-Stage, Optimized Build (Chapters 1โ2)
Dependencies are copied and installed before the source (Chapter 2's cache-ordering rule), the final stage only receives compiled output and production dependencies (Chapter 1), and the container already runs as a non-root user (Chapter 6, folded in early rather than bolted on later).
Step 2: Compose With Healthchecks & Named Resources (Chapters 3โ5)
The database's actual data lives in a named volume (Chapter 5), the two services share an explicitly named custom network (Chapter 4), and api waits for db's healthcheck to genuinely pass (Chapter 3) rather than merely starting.
Step 3: Security Hardening (Chapter 6)
The api container's filesystem is read-only at runtime, with /tmp mounted as tmpfs for anything genuinely needing to write. DATABASE_URL is injected from the environment, never baked into the image or the Compose file itself.
Step 4: CI Integration (Chapter 7)
Putting It All Together
| Course Concept | Where It's Used in This App |
|---|---|
| Multi-stage builds (Ch.1) | Builder stage compiles; final stage ships only dist + production deps |
| Cache-ordering / base image choice (Ch.2) | Dependencies copied before source; slim final base |
Healthchecks + depends_on (Ch.3) | api waits for db's real readiness, not just container start |
| Named networks (Ch.4) | taskapi-backend โ isolates and connects the two services |
| Named volumes (Ch.5) | taskapi-db-data โ the database's data survives container removal |
| Non-root, read-only, no baked-in secrets (Ch.6) | appuser, read_only: true, DATABASE_URL from environment |
| SHA/latest tagging, registry push, scan gate (Ch.7) | The full CI build/scan/push sequence |
๐ป Coding Challenges
Challenge 1: Add a .dockerignore
Write a .dockerignore file for this capstone's build context, excluding node_modules, .git, and any local .env file.
Goal: Practice applying Chapter 2's .dockerignore guidance to this specific project.
Challenge 2: Add an Override File for Local Development
Write a docker-compose.override.yml that bind-mounts the local source directory into api for live-reloading during development, without changing the base docker-compose.yml.
Goal: Practice combining Chapter 3's override-file pattern with Chapter 5's bind-mount use case, layered on top of this capstone's base file.
Challenge 3: Explain the Registry Authentication Step
Extend Step 4's CI snippet with the registry login step, and explain why it must never hardcode credentials directly in the pipeline file.
Goal: Practice applying Chapter 7's credential-handling guidance to this capstone's own CI step.
It can look contradictory at first glance: db writes real data to disk constantly, yet Chapter 6's read_only hardening seems to say containers shouldn't write anything. The resolution is that read_only locks down the container's own filesystem layer โ it says nothing about a separately mounted named volume, which remains fully writable regardless. If db here were hardened with read_only: true, its data directory would still need an explicit writable exception (either accepting that the volume mount itself isn't affected by read_only, or adding a tmpfs/volume override for the specific paths that must write) โ the two techniques from Chapters 5 and 6 are complementary, not contradictory, but combining them correctly requires understanding that a volume mount and the container's own filesystem are genuinely separate things.
๐ Course Complete
That's the full Docker Intermediate/Advanced course โ from multi-stage builds and image optimization through Compose in depth, container networking, production data management, security hardening, CI integration, and orchestration orientation, finishing with a real, production-shaped multi-container application. Together with Docker for Beginners, this completes both Docker courses on the site.