Devops
Difficulty: Advanced
17 min read

Docker in Production: BuildKit, Compose v2, Multi-stage and Security

Master Docker in a production environment with BuildKit, multi-stage builds, Compose v2, image security and monitoring. A complete and pragmatic guide.

Back to tutorials
Prerequisites
This tutorial assumes a basic knowledge of Docker (images, containers, Dockerfile). If you are just starting out, first read the Docker installation and configuration guide.

Docker in development vs Docker in production

Most teams use Docker in development without difficulty. A docker compose up, a few mounted volumes, and everything works. But moving to production reveals very different challenges: build time, image size, security, resource management, monitoring and reliability.

In development, you tolerate 2 GB images, 10-minute builds and containers running as root. In production, every megabyte counts, every second of build impacts the deployment cycle, and every root container is a potential vulnerability.

This guide covers the techniques and tools essential to running Docker professionally: BuildKit for fast and secure builds, multi-stage builds for minimal images, Compose v2 for orchestration, and the best practices for security, monitoring and networking.

BuildKit: the next-generation build engine

BuildKit has been the default build engine since Docker 23.0 (January 2023). It replaces the legacy builder and brings major improvements in terms of performance and security. If you are using a recent version of Docker, BuildKit is already active.

Enabling and configuring

Check that BuildKit is properly enabled on your system:

# Check the version and the active builder
docker buildx version
docker buildx ls

# Force BuildKit for older versions
export DOCKER_BUILDKIT=1

# Or in /etc/docker/daemon.json
{
  "features": {
    "buildkit": true
  }
}

Create a dedicated builder with advanced options for production:

# Create a builder with a larger cache
docker buildx create --name production-builder \
  --driver docker-container \
  --config /etc/buildkitd.toml \
  --use

# buildkitd.toml configuration
[worker.oci]
  max-parallelism = 4
  gc = true
  gckeepstorage = 10000  # 10 GB of cache
  [[worker.oci.gcpolicy]]
    keepBytes = 5000000000  # 5 GB minimum
    keepDuration = 604800   # 7 days

Cache mounts: speeding up builds

Cache mounts are the most impactful BuildKit feature. They allow package manager caches to persist between builds, eliminating repetitive downloads.

# syntax=docker/dockerfile:1

FROM python:3.12-slim AS builder

WORKDIR /app
COPY requirements.txt .

# Cache mount for pip: downloaded packages are reused
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install --no-compile -r requirements.txt

# Equivalent for Node.js
FROM node:20-alpine AS node-builder
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci --production

# Equivalent for apt
FROM debian:bookworm-slim
RUN --mount=type=cache,target=/var/cache/apt \
    --mount=type=cache,target=/var/lib/apt/lists \
    apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates
Real-world impact
Cache mounts typically reduce rebuilds by 60 to 90%. A 3-minute pip install drops to 10 seconds when the packages are cached.

Secret mounts: injecting credentials without exposing them

The classic problem: your build needs a token to access a private registry, a Git repo, or an API. With the old builder, the secret ended up in an image layer. BuildKit solves this problem with secret mounts.

# syntax=docker/dockerfile:1

FROM python:3.12-slim

# The secret is mounted read-only, never stored in a layer
RUN --mount=type=secret,id=pip_conf,target=/etc/pip.conf \
    pip install -r requirements.txt

# Access to a private repo via SSH
RUN --mount=type=ssh \
    git clone [email protected]:private/repo.git
# Pass the secret to the build
docker buildx build \
  --secret id=pip_conf,src=$HOME/.pip/pip.conf \
  --ssh default=$SSH_AUTH_SOCK \
  -t myapp:latest .
Warning
Never use ARG or ENV to pass secrets. These values are visible with docker history and stored in the image metadata.

Parallel builds and distributed cache

BuildKit automatically parallelizes the independent stages of a Dockerfile. Combined with a distributed cache via a registry, builds become fast even on ephemeral CI runners.

# Build with distributed cache via a registry
docker buildx build \
  --cache-from type=registry,ref=registry.example.com/myapp:cache \
  --cache-to type=registry,ref=registry.example.com/myapp:cache,mode=max \
  -t myapp:latest \
  --push .

# Local cache for frequent builds
docker buildx build \
  --cache-from type=local,src=/tmp/buildcache \
  --cache-to type=local,dest=/tmp/buildcache,mode=max \
  -t myapp:latest .

The max mode exports all the intermediate layers, not just those of the final image. This maximizes cache hits for the build stages.

Premium Content

This advanced tutorial is reserved for premium members.

9,90€ / month
  • All advanced tutorials
  • New content every week
  • Progress tracking
  • Cancel anytime

Written by

Morgann Riu

Cybersecurity and Linux administration expert. I share my knowledge through free tutorials and training to help system administrators and developers secure their infrastructures.

Share this tutorial

Did you enjoy this article?

Comments

Checklist Sécurité Linux

30 points essentiels pour sécuriser un serveur Linux. Recevez aussi les nouveaux tutoriels par email.

Pas de spam. Désabonnement en 1 clic.