🎁 New User? Get 20% off your first purchase with code NEWUSER20 Register Now →
Menu

Categories

Dockerfile Best Practices: Building Optimized Container Images in 2026

Dockerfile Best Practices: Building Optimized Container Images in 2026

Writing a Dockerfile is easy. Writing a good Dockerfile that produces optimized, secure, and maintainable images requires knowledge of best practices and common pitfalls. In this guide, we cover the essential techniques for building production-ready container images in 2026.

Start with the Right Base Image

Your base image choice has a massive impact on image size and security:

# Bad: Full Ubuntu image (~77MB)
FROM ubuntu:22.04

# Better: Slim variant (~25MB)
FROM python:3.12-slim

# Best for compiled languages: Distroless (~2MB)
FROM gcr.io/distroless/static-debian12

Alpine-based images are small but may cause compatibility issues with some Python packages due to musl libc. Test thoroughly before using Alpine in production.

Multi-Stage Builds

Multi-stage builds are the most powerful optimization technique. They allow you to use build tools in one stage and copy only the compiled output to the final image:

# Stage 1: Build
FROM golang:1.22 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /server

# Stage 2: Production
FROM gcr.io/distroless/static-debian12
COPY --from=builder /server /server
EXPOSE 8080
ENTRYPOINT ["/server"]

Optimize Layer Caching

Docker caches each layer. Order your instructions from least to most frequently changing:

# Good: Dependencies cached separately from code
FROM node:20-slim
WORKDIR /app

# These change rarely - cached
COPY package.json package-lock.json ./
RUN npm ci --production

# This changes frequently - not cached
COPY . .
RUN npm run build

Minimize Layer Count

Combine related commands into single RUN instructions:

# Bad: Multiple layers
RUN apt-get update
RUN apt-get install -y curl wget
RUN apt-get clean

# Good: Single layer with cleanup
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    curl wget && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

Security Best Practices

Running containers as root is a security risk. Always create and use a non-root user:

FROM python:3.12-slim

RUN groupadd -r appuser && useradd -r -g appuser appuser
WORKDIR /app
COPY --chown=appuser:appuser . .
RUN pip install --no-cache-dir -r requirements.txt

USER appuser
CMD ["python", "app.py"]

Use .dockerignore

Always include a .dockerignore file to exclude unnecessary files from the build context:

.git
.env
node_modules
__pycache__
*.md
docker-compose*.yml
.github

Health Checks

Add HEALTHCHECK instructions so Docker can monitor container health:

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

Environment Variables and Secrets

Never hardcode secrets in Dockerfiles. Use build arguments for non-sensitive configuration and runtime environment variables for secrets:

ARG APP_VERSION=latest
ENV APP_VERSION=${APP_VERSION}

# Never do this:
# ENV DATABASE_PASSWORD=mysecret

Image Scanning

Regularly scan your images for vulnerabilities:

docker scout cves myimage:latest
trivy image myimage:latest

Checklist for Production Dockerfiles

  1. Use specific image tags, never latest
  2. Implement multi-stage builds
  3. Run as non-root user
  4. Include health checks
  5. Minimize installed packages
  6. Use .dockerignore
  7. Scan for vulnerabilities regularly
  8. Label images with metadata

Following these best practices will result in container images that are smaller, faster to build, more secure, and easier to maintain. Start applying them to your projects today and see the difference in your deployment pipeline.

Share this article:

Stay Updated

Subscribe to our newsletter for the latest tutorials, tips, and exclusive offers.