Production Best Practices

Follow a checklist of habits for building images and running containers that are small, secure and reliable in production.

Running Docker in production well comes down to a handful of good habits. This lesson is an overview; the rest of this chapter digs into each one.

The checklist

  • Small images — use minimal base images and multi-stage builds.
  • One process per container — each container should do one job.
  • Run as non-root — drop privileges with the USER instruction.
  • Health checks — let Docker know when your app is actually ready.
  • Pin versions — avoid latest; use explicit tags.
  • Handle signals — shut down cleanly on SIGTERM.
  • Keep secrets out — never bake credentials into images.
  • Log to stdout — let the platform collect logs.

Example

Example · dockerfile
# A production-minded Dockerfile applying several best practices
FROM node:20-alpine

ENV NODE_ENV=production
WORKDIR /app

COPY package*.json ./
RUN npm ci --omit=dev

COPY . .

# Run as an unprivileged user
USER node

EXPOSE 3000
HEALTHCHECK CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "server.js"]

When to use it

  • A team adopts a production Dockerfile checklist (non-root user, health check, .dockerignore, pinned base) to pass a security audit.
  • A DevOps engineer reduces average image size from 1.2 GB to 180 MB across all services by applying multi-stage build and slim-base practices.
  • A startup enforces best-practice linting of Dockerfiles in CI using hadolint so issues are caught before they reach production.

More examples

Lint a Dockerfile with hadolint

Runs hadolint in a container to check the Dockerfile against best-practice rules without installing the tool.

Example · bash
docker run --rm -i hadolint/hadolint < Dockerfile

Production-ready Dockerfile checklist

Combines multi-stage build, non-root user, health check, and production-only dependencies in one Dockerfile.

Example · dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .

FROM node:20-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --from=builder --chown=app:app /app .
USER app
HEALTHCHECK CMD wget -qO- http://localhost:3000/health || exit 1
EXPOSE 3000
CMD ["node", "index.js"]

Run container with read-only root filesystem

Makes the container's filesystem read-only and mounts tmpfs only for directories that need writes.

Example · bash
docker run -d \
  --read-only \
  --tmpfs /tmp \
  --tmpfs /var/run \
  --name api \
  myapp:latest

Discussion

  • Be the first to comment on this lesson.