Kubernetes & Container Orchestration

Docker Best Practices for Production

MatterAI
MatterAI
12 min read·

Docker Best Practices for Production

Most Dockerfiles in production are wrong in the same ways: images are bloated, run as root, rebuild everything on every change, and have no health checks. This guide covers the practices that matter: smaller images, faster builds, safer defaults, and images that behave well under orchestration.

Practice 1: Multi-Stage Builds

The single highest-impact practice. Build in one stage with all the tooling, copy only the artifacts into a minimal runtime stage:

# Stage 1: build
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: runtime — only the built output and production deps
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/package.json ./
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]

The result: the runtime image contains no source code, no build tools, no dev dependencies. A typical Node image drops from 1.5 GB to under 200 MB.

Practice 2: Use a .dockerignore

The build context is sent to the daemon on every build. Exclude everything the image does not need:

node_modules
dist
.git
.gitignore
*.log
.env
.env.*
Dockerfile
.dockerignore
coverage

This speeds up builds and prevents secrets in .env from leaking into image layers.

Practice 3: Run as Non-Root

Containers default to root. A compromised container running as root is a compromised host. Create a user and switch:

FROM python:3.12-slim
WORKDIR /app

# Create a non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
USER appuser

EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

For Node images, the node user already exists:

FROM node:22-alpine
WORKDIR /app
COPY --chown=node:node . .
USER node
CMD ["node", "server.js"]

Practice 4: Pin Base Image Digests

node:22-alpine changes under you. Pin to a digest for reproducible builds:

FROM node:22-alpine@sha256:9c6b1f2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a

Use a tool like Renovate or Dependabot to keep digests updated automatically. Tag-pinned images are a supply chain risk: the tag can be repointed to a compromised image.

Practice 5: Health Checks

Orchestrators need to know when your container is actually ready. Add a health check to the image:

FROM node:22-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
USER node
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD node -e "fetch('http://localhost:3000/healthz').then(r => { if (!r.ok) process.exit(1) }).catch(() => process.exit(1))"
CMD ["node", "dist/server.js"]

And expose readiness/liveness probes in Kubernetes:

readinessProbe:
  httpGet:
    path: /healthz
    port: 3000
  initialDelaySeconds: 5
  periodSeconds: 10
livenessProbe:
  httpGet:
    path: /healthz
    port: 3000
  initialDelaySeconds: 15
  periodSeconds: 20

Practice 6: Layer Ordering for Cache Efficiency

Docker caches layers. Order instructions from least to most frequently changing:

# 1. Dependencies first (rarely change)
COPY package.json package-lock.json ./
RUN npm ci

# 2. Source code last (changes every commit)
COPY . .

A change to source code reuses the dependency layer. A change to package.json invalidates only from that point down. This turns rebuilds from minutes into seconds.

Practice 7: Minimize Attack Surface

  • Use slim/alpine base images: fewer packages, fewer vulnerabilities.
  • Install only production dependencies: npm ci --omit=dev, pip install --no-cache-dir.
  • Remove package managers and shells from runtime images where possible (distroless images do this by default).
  • Set read-only filesystem in orchestration: readOnlyRootFilesystem: true.

Distroless images are the extreme version:

FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM gcr.io/distroless/nodejs22-debian12
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
CMD ["dist/server.js"]

No shell, no package manager, no root. If an attacker gets code execution, there is nothing to escalate with.

Practice 8: Scan Images in CI

Scan every image before it ships:

# .github/workflows/scan.yml
name: Scan Images
on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        run: docker build -t myapp:ci .
      - name: Scan with Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:ci
          severity: HIGH,CRITICAL
          exit-code: "1"

Fail the build on high and critical findings. Fix or document every finding; do not let the scan become noise.

Practice 9: Metadata and Labels

Labels make images auditable:

LABEL org.opencontainers.image.source="https://github.com/acme/myapp"
LABEL org.opencontainers.image.version="1.2.0"
LABEL org.opencontainers.image.revision="abc123"

Implementation Checklist

  • Multi-stage builds: build stage + minimal runtime stage
  • .dockerignore excludes node_modules, .git, .env
  • Non-root user in every image
  • Base image digests pinned and auto-updated
  • HEALTHCHECK instruction + k8s probes
  • Dependencies before source for cache efficiency
  • Slim/distroless base, production-only deps
  • Trivy/Grype scan failing CI on high/critical
  • Labels for provenance

MatterAI builds frontier AI infrastructure for engineering teams — from inference-optimized models to autonomous coding agents and agentic code reviews.

Explore what we're building:

  • Orbital IDE — Autonomous AI coding agent with background agents and deep codebase memory
  • AI Code Reviews — Agentic pre-commit reviews across GitHub, GitLab, and Bitbucket
  • Axon Models — Frontier-grade reasoning models at 70% lower inference cost

Get started free - https://app.matterai.so


Follow us on X · LinkedIn · GitHub

Share this Guide:

Ship Faster. Ship Safer.

Join thousands of engineering teams using MatterAI to autonomously build, review, and deploy code with enterprise-grade precision.

No credit card requiredSOC 2 Type IISetup in 2 min