Docker Best Practices for Production
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
Share this Guide:
More Guides
Local LLMs in Your IDE: Connecting Ollama to Coding Agents and Autocomplete
Wire local models into VS Code, JetBrains, Cline, Continue, and Aider via the OpenAI-compatible API. Covers model routing, context budgets, tool calling with small models, and when a local model is the right choice for the job.
15 min readBuilding a Self-Hosted AI Stack: Ollama, Open WebUI, and Local RAG
Stand up a fully self-hosted AI stack on a single machine: Ollama for inference, Open WebUI as the chat interface, local embeddings for RAG, and a reverse proxy for secure access. No cloud dependency, no data leaving your network.
17 min readTop 5 Open-Source Coding Models to Run on Your Mac (2026)
The best local coding models for Apple Silicon in 2026, ranked by quality per gigabyte of unified memory. Covers qwen3-coder, devstral, gpt-oss, and more with real pull tags, sizes, and context windows.
14 min readRunning LLMs Locally: GGUF, Quantization, and Memory Planning
Learn the GGUF format, the quantization ladder from Q2 to FP16, and the exact memory math for running models on Apple Silicon and NVIDIA GPUs. Includes Ollama and llama.cpp tuning for KV cache and context.
15 min readOllama vs vLLM vs llama.cpp: Choosing the Right Local LLM Runtime
Compare the three dominant local LLM runtimes on architecture, throughput, hardware, and deployment context. Includes benchmark data, a decision framework, and a migration path from Ollama to vLLM.
16 min readContinue Reading
Local LLMs in Your IDE: Connecting Ollama to Coding Agents and Autocomplete
Wire local models into VS Code, JetBrains, Cline, Continue, and Aider via the OpenAI-compatible API. Covers model routing, context budgets, tool calling with small models, and when a local model is the right choice for the job.
15 min readBuilding a Self-Hosted AI Stack: Ollama, Open WebUI, and Local RAG
Stand up a fully self-hosted AI stack on a single machine: Ollama for inference, Open WebUI as the chat interface, local embeddings for RAG, and a reverse proxy for secure access. No cloud dependency, no data leaving your network.
17 min readTop 5 Open-Source Coding Models to Run on Your Mac (2026)
The best local coding models for Apple Silicon in 2026, ranked by quality per gigabyte of unified memory. Covers qwen3-coder, devstral, gpt-oss, and more with real pull tags, sizes, and context windows.
14 min readShip Faster. Ship Safer.
Join thousands of engineering teams using MatterAI to autonomously build, review, and deploy code with enterprise-grade precision.
