Docker Best Practices: Smaller, Safer, Faster Images
Docker Best Practices: Smaller, Safer, Faster Images
A 1.2 GB image is rarely necessary. Here is the playbook we apply to every Dockerfile.
1. Multi-stage builds
Build with the full SDK, ship with the runtime only:
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM gcr.io/distroless/nodejs20
COPY --from=build /app/dist /app
CMD ["/app/server.js"]
2. Order layers by stability
COPY package*.json before COPY . so dependency layers cache across code changes.
3. Use .dockerignore
Exclude node_modules, .git, .env, dist, and tests. This dramatically shrinks the build context.
4. Pin everything
FROM node:20.11.0-bookworm-slim@sha256:abcd...
5. Run as non-root
RUN useradd -m app
USER app
Distroless and Chainguard images do this for you.
6. Health checks
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://localhost:3000/health || exit 1
7. Scan and sign
docker scoutortrivyfor vulnerability scanning.cosign signfor supply-chain attestations.
TL;DR
Multi-stage + distroless + pinned digests is the 80/20 of secure, lean container images.
Found this helpful? Try our free tools!
Explore Our Tools →