5 Practical Docker Tips from the Captain’s Chair
Five Docker Captains share practical advice on SBOM attestations, build debugging, front-end workflows, clear Dockerfiles, and multi-stage builds.
Docker Captains spend their time solving container problems in very different environments: cloud platforms, front-end teams, Rails applications, open-source projects, and secure software delivery.
Docker's From the Captain's Chair series explores those backgrounds, but each interview also asks a particularly useful question: What is one Docker tip you wish every developer knew?
Here are five answers worth putting into practice.
Key Takeaways
- Generate the SBOM during the build and scan earlier stages so build-time dependencies remain visible after the final image discards them.
- Use Docker DX build debugging to understand failed builds rather than repeatedly editing and rebuilding blind.
- Treat Docker as a development tool for front-end teams, not only as back-end or infrastructure machinery.
- Keep Dockerfiles simple enough that every instruction has an obvious purpose.
- Use multi-stage builds to keep compilers, build tools, and development dependencies out of production images.
1. Generate a Complete SBOM During the Build
In my Captain's Chair interview, my tip was to generate a Software Bill of Materials attestation when building an image. The important part is not merely automating another step. It is capturing dependencies that cannot be recovered by scanning the finished image.
In a multi-stage build, compilers, package managers, and build dependencies often exist only in an earlier stage. The final stage receives the compiled output and leaves that toolchain behind. A scanner examining only the final image cannot inventory files and packages that are no longer there.
BuildKit can scan those dependencies while the relevant stages still exist. Its default SBOM covers only the final stage, so opt the earlier stages into scanning with the special BUILDKIT_SBOM_SCAN_STAGE build argument:
# syntax=docker/dockerfile:1
ARG BUILDKIT_SBOM_SCAN_STAGE=true
FROM node:24-alpine AS build
# Install dependencies and build the application.
FROM nginxinc/nginx-unprivileged:alpine AS runtime
# Copy only the runtime artifact from the build stage.
Then request the SBOM attestation as part of the build:
docker buildx build --sbom=true -t example/app:latest --push .
The resulting attestations preserve an inventory for the final image and the selected build stages. This exposes build-time dependencies that could affect the artifact even though they are absent from its runtime filesystem.
An SBOM does not prove that every component is safe, but it gives scanners, policy engines, and incident responders evidence that a post-build scan cannot reconstruct. See Docker's SBOM attestation documentation for stage-specific controls and output options.
2. Debug Docker Builds Instead of Guessing
Naga Santhosh Reddy Vootukuri recommends the Docker DX extension for Visual Studio Code, particularly its live build-debugging workflow.
AI can generate a plausible Dockerfile quickly. It cannot guarantee that the result has sensible layers, effective caching, correct paths, or secure runtime behavior. When the build fails, changing instructions at random usually produces a longer Dockerfile without producing a better mental model.
A debugger lets you inspect the build where it fails. Use that feedback to understand the state of the filesystem, the active stage, and the instruction that introduced the problem. The goal is not merely to get one green build; it is to know why the build works.
3. Bring Docker into Front-End Development
Kristiyan Velkov argues that Docker is useful for everyone, including front-end developers.
Modern front-end projects have their own runtime versions, package managers, native dependencies, build tools, environment variables, and deployment assumptions. Leaving all of that implicit creates the same environment drift that back-end teams have dealt with for years.
A production-ready container workflow gives the front-end team:
- the same toolchain locally and in CI;
- a documented build process;
- a repeatable production artifact;
- a shared boundary between application and platform teams.
Containerization should not remove front-end developers from the deployment process. It should give them a reproducible way to participate in it.
4. Make Your Dockerfile Boring
Igor Aleksandrov offers a useful warning sign: when something in a Dockerfile looks strange, step back and question the design.
Long shell chains, surprising file permissions, unexplained downloads, and unusual copy patterns make builds harder to review and maintain. They can also hide security mistakes in places reviewers are likely to skim.
A good Dockerfile should read as a short sequence of intentional decisions:
- Start from a known base image.
- Install only the dependencies the stage needs.
- Copy files deliberately to preserve caching.
- Build the application.
- Define an unambiguous runtime.
Complex applications do not always produce simple builds, but accidental complexity is still a design smell. Every unusual instruction should have a reason another developer can understand.
5. Separate Building from Running
Pradumna Saraf recommends multi-stage builds. He has seen the approach reduce an image from 1.7 GB to under 100 MB.
The exact reduction depends on the application, but the mechanism is consistent: compile or bundle the application in one stage, then copy only the runtime output into a smaller final stage.
FROM node:24-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginxinc/nginx-unprivileged:alpine AS runtime
COPY --from=build /app/dist /usr/share/nginx/html
The final image does not need the source tree, package manager cache, compiler, or development dependencies used during the build. Removing them reduces image size, shortens transfers, and leaves fewer tools and packages available to an attacker.
Adapt the output directory and runtime image to your application, but keep the boundary clear: the production container should contain what the application needs to run, not everything needed to build it.
One Workflow, Five Habits
These five tips reinforce one another:
- define the build clearly;
- debug it with evidence;
- make it reproducible for every team;
- keep its instructions understandable;
- ship only the runtime result;
- attach an inventory to what you ship.
That is the larger lesson from the Captain's Chair: good Docker practice is not a collection of clever tricks. It is the discipline of making software delivery explicit, observable, and repeatable.
