Docker for Frontend Developers: The Minimum You Need
Enough Docker to containerize your frontend app without reading the entire documentation. Multi-stage builds included.
The Pattern
The multi-stage build is the only pattern most frontend developers need to know well: a first stage builds the application using a node:alpine base image, and a second, separate stage serves the compiled static output using an nginx:alpine base image. Because the final image only includes the second stage, the build tooling, node_modules, and source files from the first stage never make it into the deployed artifact, which brings the final image size down to roughly 30MB instead of the 1.2GB or more you'd get by shipping the full Node build environment. The size difference matters for deployment speed (smaller images push and pull faster) and for security (a smaller image has a smaller attack surface).
The Dockerfile is only about 20 lines: the build stage starts `FROM node:alpine as build`, copies package.json, runs `npm ci` for a clean, reproducible install, copies the rest of the source, and runs `npm run build`. Using `npm ci` instead of `npm install` is important for reproducibility — `npm ci` reads from package-lock.json and installs exactly the versions specified, while `npm install` may update the lockfile with newer versions. In CI/CD, you want reproducibility, not "the latest compatible versions," because a build that produces different artifacts depending on when it ran is much harder to debug.
The second stage starts `FROM nginx:alpine`, copies the compiled output from the build stage's dist directory into nginx's html directory, and copies over a custom nginx.conf to handle routing correctly for a single-page application. The custom nginx config is the part most frontend developers get wrong on the first try — the default doesn't handle client-side routing, so any deep link to a route other than `/` returns a 404. The fix is a fallback rule that serves index.html for any path that doesn't match a static file. Add a .dockerignore excluding node_modules, .git, and .env files, and the setup is complete — a CI pipeline builds the image on every push, pushes to a registry, and triggers a deploy. Total setup time: about 30 minutes.
