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
USERinstruction. - 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
# 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.
docker run --rm -i hadolint/hadolint < DockerfileProduction-ready Dockerfile checklist
Combines multi-stage build, non-root user, health check, and production-only dependencies in one 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.
docker run -d \
--read-only \
--tmpfs /tmp \
--tmpfs /var/run \
--name api \
myapp:latest
Discussion