hello-docker · your first optimised container with multi-stage builds and BuildKit
Build a single-service Dockerfile that shrinks from a bloated 900 MB base image to under 50 MB using multi-stage builds, a distroless or alpine runtime stage, and a non-root USER directive enforced by BuildKit.
Every production frontend and API team applies the multi-stage build pattern to separate the heavy build-time toolchain from the lean runtime image. Without it, a Node or Python application ships its entire compiler, package manager, and dev dependencies into production, inflating attack surface and registry storage costs. Teams at companies of all sizes use a builder stage to compile or bundle assets and a minimal alpine or distroless runtime stage to serve them, routinely cutting image sizes from hundreds of megabytes to tens of megabytes. Running the final container as a non-root user is a baseline security control required by most enterprise security policies and enforced by Kubernetes admission controllers in production clusters.
A hiring manager reviewing this project immediately sees that the engineer understands the full image build lifecycle, not just how to write a working Dockerfile. Demonstrating a before-and-after size reduction with a documented multi-stage strategy signals familiarity with BuildKit layer caching, stage naming, COPY --from mechanics, and the principle of least privilege via USER. These are the exact skills listed in Docker Mastery (Bret Fisher, 318K+ students), the Coursera Docker roadmap, and the roadmap.sh Docker path as non-negotiable for anyone shipping containers to production. It also establishes a clean, reusable Dockerfile template that every subsequent portfolio project can extend.
- Write a naive single-stage Dockerfile using a full node:22 base image and measure its size with docker image ls to establish the bloated baseline.
- Refactor the Dockerfile into a two-stage build: a named builder stage that installs dependencies and compiles or bundles the application, and a minimal nginx:1.27-alpine or node:22-alpine runtime stage that copies only the compiled output.
- Add a non-root USER directive to the runtime stage, verify the running container process is unprivileged with docker exec, and document the security rationale in a README.
- Enable BuildKit via DOCKER_BUILDKIT=1 or docker buildx build, add a .dockerignore file to exclude node_modules and local config, and confirm layer cache reuse on a second build by observing CACHED log lines.
- Compare final image size against the baseline, tag the optimised image with a descriptive label using LABEL and --tag, and write a short README section explaining each Dockerfile instruction and the size reduction achieved.
hello-docker · your first optimised container with multi-stage builds and BuildKit
Build a single-service Dockerfile that shrinks from a bloated 900 MB base image to under 50 MB using multi-stage builds, a distroless or alpine runtime stage, and a non-root USER directive enforced by BuildKit.
Every production frontend and API team applies the multi-stage build pattern to separate the heavy build-time toolchain from the lean runtime image. Without it, a Node or Python application ships its entire compiler, package manager, and dev dependencies into production, inflating attack surface and registry storage costs. Teams at companies of all sizes use a builder stage to compile or bundle assets and a minimal alpine or distroless runtime stage to serve them, routinely cutting image sizes from hundreds of megabytes to tens of megabytes. Running the final container as a non-root user is a baseline security control required by most enterprise security policies and enforced by Kubernetes admission controllers in production clusters.
A hiring manager reviewing this project immediately sees that the engineer understands the full image build lifecycle, not just how to write a working Dockerfile. Demonstrating a before-and-after size reduction with a documented multi-stage strategy signals familiarity with BuildKit layer caching, stage naming, COPY --from mechanics, and the principle of least privilege via USER. These are the exact skills listed in Docker Mastery (Bret Fisher, 318K+ students), the Coursera Docker roadmap, and the roadmap.sh Docker path as non-negotiable for anyone shipping containers to production. It also establishes a clean, reusable Dockerfile template that every subsequent portfolio project can extend.
- Write a naive single-stage Dockerfile using a full node:22 base image and measure its size with docker image ls to establish the bloated baseline.
- Refactor the Dockerfile into a two-stage build: a named builder stage that installs dependencies and compiles or bundles the application, and a minimal nginx:1.27-alpine or node:22-alpine runtime stage that copies only the compiled output.
- Add a non-root USER directive to the runtime stage, verify the running container process is unprivileged with docker exec, and document the security rationale in a README.
- Enable BuildKit via DOCKER_BUILDKIT=1 or docker buildx build, add a .dockerignore file to exclude node_modules and local config, and confirm layer cache reuse on a second build by observing CACHED log lines.
- Compare final image size against the baseline, tag the optimised image with a descriptive label using LABEL and --tag, and write a short README section explaining each Dockerfile instruction and the size reduction achieved.