Docker BuildKit and buildx

Khimananda Oli 7 min read Database
Docker BuildKit and buildx

By Khimananda Oli | Last reviewed: August 2026

Slow container builds and fragile CI pipelines remain primary bottlenecks for teams shipping software at scale. Docker BuildKit and buildx solve this by replacing the legacy sequential builder with a concurrent, graph-based execution engine that supports multi-platform targeting, advanced caching, and secure secret handling natively. If you are still running plain docker build commands in 2026, you are leaving significant performance and security gains on the table.

How does Docker BuildKit and buildx improve build performance?

The fundamental difference between the legacy Docker builder and Docker BuildKit and buildx lies in how they interpret and execute your Dockerfile. The old builder processed instructions sequentially, rebuilding entire layers whenever a cache miss occurred, even if subsequent steps were independent. BuildKit treats your Dockerfile as a dependency graph (LLB - Low-Level Build), allowing it to identify and execute independent stages concurrently.

Legacy Builder (Sequential)Stage 1: DependenciesStage 2: Compile (Blocked)Stage 3: Assets (Blocked)Stage 4: Final AssemblyTotal Time: Sum of all stagesBuildKit (Parallel Graph)Stage 1: DependenciesStage 2: Compile (Parallel)Stage 3: Assets (Parallel)Stage 4: Final AssemblyTotal Time: Longest critical path only
Docker BuildKit and buildx execute independent build stages concurrently, reducing total build time significantly compared to legacy sequential processing.

In practice, this means if your Dockerfile has separate stages for compiling Go binaries and building frontend assets, BuildKit runs them simultaneously rather than waiting for one to finish before starting the other. For complex microservices or monorepos, this alone can cut build times by 30–50%. Beyond parallelism, BuildKit introduces intelligent cache invalidation. It tracks content hashes rather than just timestamps, so copying a README into your context won't invalidate your npm install layer—a common frustration with the legacy builder.

To leverage these improvements locally after you install Docker on Ubuntu, ensure BuildKit is enabled. In modern Docker Desktop and Engine versions (23.0+), it is the default. You can verify or force it via environment variable:

<!-- Enable BuildKit explicitly -->
export DOCKER_BUILDKIT=1

<!-- Or configure permanently in /etc/docker/daemon.json -->
{
  "features": {
    "buildkit": true
  }
}

How do you configure multi-platform builds with Docker buildx?

Multi-platform support is the killer feature of Docker BuildKit and buildx. With Apple Silicon Macs, ARM-based cloud instances (AWS Graviton, Azure Cobalt), and edge devices all gaining traction, shipping single-architecture images is no longer acceptable. The buildx CLI wraps BuildKit to make cross-compilation manageable through QEMU emulation or native remote builders.

Setting up a multi-platform builder instance

You cannot use the default Docker driver for multi-platform builds. Create a dedicated builder instance with the docker-container driver, which runs BuildKit inside a privileged container:

# Create and bootstrap a new builder
docker buildx create --name multiarch --driver docker-container --bootstrap

# Verify supported platforms
docker buildx inspect multiarch --bootstrap

# Set as default (optional)
docker buildx use multiarch --default

Building and pushing multi-arch images

When targeting multiple architectures, you must push directly to a registry. Multi-platform manifests cannot be stored in the local Docker image store. Use the --platform flag with comma-separated targets:

docker buildx build \
  --platform linux/amd64,linux/arm64,linux/arm/v7 \
  --tag registry.example.com/myapp:v1.2.0 \
  --push \
  .

A common mistake is attempting to test multi-platform images locally without specifying the platform. Always validate the specific architecture you need using docker run --platform linux/arm64 ... to avoid false positives from automatic emulation masking issues.

Source Code& Dockerfilebuildx Builderlinux/amd64linux/arm64linux/arm/v7Container RegistryManifest List (OCI Index)amd64 Blobarm64 Blobarm/v7 Blob
Docker buildx orchestrates parallel compilation for each target architecture and pushes a unified OCI manifest list to the registry.

What are the best caching strategies for Docker BuildKit in CI?

Caching is where Docker BuildKit and buildx delivers its highest ROI in continuous integration environments. Unlike the legacy builder which relies solely on local layer caches (often lost between ephemeral CI runners), BuildKit supports external cache backends. This persistence across pipeline runs prevents redundant work like reinstalling dependencies or recompiling unchanged modules.

  • Inline Cache: Embeds cache metadata directly into the final image. Best for simple projects where image size overhead (~5-10%) is acceptable. Enables any pull to serve as a cache source.
  • Registry Cache: Stores cache layers separately in a registry tag. Ideal for large monorepos or heavy compilation tasks. Keeps production images lean while maintaining fast rebuilds.
  • Local/Volume Cache: Persists cache on disk or named volumes. Perfect for self-hosted runners or local development where network latency to a registry is undesirable.
  • GHA/S3/Azure Cache: Native integrations with GitHub Actions, AWS S3, or Azure Blob Storage. Eliminates registry overhead entirely for cloud-native CI pipelines.

For most teams using GitHub Actions, the GHA cache backend offers the best balance of speed and cost. Here is a production-ready configuration:

docker buildx build \
  --cache-from type=gha \
  --cache-to type=gha,mode=max \
  --tag myapp:${{ github.sha }} \
  --push \
  .

The mode=max parameter is critical—it exports cache for all intermediate layers, not just the final stage. Without it, expensive compilation steps in early stages may still re-run. For self-hosted runners on persistent infrastructure, consider combining volume mounts with registry fallback as described in our guide to speeding up CI builds with caching.

How do you manage secrets securely during Docker builds?

Secret management during builds has historically been a major security gap. Developers would copy .env files or embed API keys in layers, leaving credentials exposed in image history even after deletion. Docker BuildKit and buildx solves this with mounted secrets and SSH agents that never persist to disk or image layers.

Using BuildKit secret mounts

Secrets are passed at build time via the --secret flag and accessed through a temporary mount. They exist only in memory during the specific RUN instruction:

# syntax=docker/dockerfile:1
FROM node:20-alpine AS builder
WORKDIR /app

# Mount secret as file, never written to layer
RUN --mount=type=secret,id=npm_token,target=/root/.npmrc \
    npm ci --production

COPY . .
RUN npm run build

Invoke the build with the secret sourced from environment variables or files:

# From environment variable
NPM_TOKEN=$(cat ~/.npmrc) docker buildx build \
  --secret id=npm_token,env=NPM_TOKEN \
  --tag myapp:secure \
  .

# From file directly
docker buildx build \
  --secret id=npm_token,src=.npmrc \
  --tag myapp:secure \
  .

SSH agent forwarding

For private Git repositories or SSH-authenticated package registries, forward your SSH agent instead of copying keys:

RUN --mount=type=ssh \
    git clone [email protected]:private-org/internal-lib.git

This pattern ensures private keys never leave your host machine. Combined with SBOM generation and signing practices covered in our DevSecOps shift-left guide, this creates an audit-ready build pipeline suitable for SOC 2 and ISO 27001 compliance frameworks.

FeatureLegacy BuilderDocker BuildKit & buildx
Execution ModelSequential, line-by-lineParallel DAG (LLB)
Multi-PlatformNot supportedNative via QEMU/remote
Cache BackendsLocal layers onlyRegistry, GHA, S3, Local, Inline
Secret HandlingARG/COPY (leaked in history)Mounted secrets (never persisted)
Build OutputLocal image onlyRegistry, OCI layout, tar, local
Skip Unused StagesNo (executes all)Yes (only required nodes)
Legacy vs BuildKit Feature MatrixLegacy BuilderBuildKit + buildxParallel Execution✗ Sequential Only✓ Full DAG ParallelismMulti-Arch Support✗ Single Platform✓ amd64/arm64/armv7External Caching✗ Local Layers Only✓ Registry/GHA/S3Secret Security✗ Leaked in History✓ Ephemeral MountsUnused Stage Skipping✗ Executes All✓ Smart Pruning
Side-by-side comparison highlighting why migrating to Docker BuildKit and buildx is essential for modern container workflows in 2026.

Adopt Docker BuildKit and buildx for Production Workflows

Migrating to Docker BuildKit and buildx is no longer optional for teams serious about velocity, security, and multi-architecture support. The transition requires updating CI configurations and potentially restructuring Dockerfiles to maximize parallelism, but the payoff in reduced pipeline duration and hardened supply chains is immediate. Start by enabling BuildKit locally, then progressively roll out multi-platform builders and external caching in your CI environment. Audit your current build times and secret handling practices—if either falls short, this toolchain is your remediation path.

Need help architecting a compliant, high-performance container build pipeline? Contact me to discuss your infrastructure requirements or explore our deep dive on multi-platform builds for advanced patterns.

Frequently Asked Questions

BuildKit is the backend engine solving dependency and caching issues, while buildx is the CLI plugin exposing those features. You use buildx commands to interact with the BuildKit daemon for multi-platform builds and advanced cache management in 2026 workflows.

Set DOCKER_BUILDKIT=1 in your shell environment or add "features":{"buildkit":true} to daemon.json. Modern Docker installations in 2026 often enable this automatically, but explicit configuration ensures consistent behavior across CI pipelines and local development environments without legacy builder fallbacks.

Yes, it supports standard syntax fully.

Buildx uses QEMU emulation or native cross-compilation via the --platform flag to build images for ARM64 and AMD64 simultaneously. It manages separate build contexts and manifests, allowing you to push a single multi-arch tag to registries efficiently without manual merging steps.

No, buildx is required for CLI access.

Ephemeral CI runners discard local cache between jobs. Configure inline cache metadata with --cache-to=type=inline or use external backends like registry or GHA types. This stores cache layers remotely, ensuring subsequent pipeline runs reuse artifacts instead of rebuilding dependencies from scratch every time.

Yes, through parallel stage execution and improved layer caching.

Use --progress=plain to see full output instead of the collapsed TUI view. Inspect specific stages with docker buildx debug in 2026 releases to step through instructions interactively. Check build logs for cache misses or network errors that the default progress bar often obscures during complex multi-stage builds.

Buildx supports local directory, inline metadata, registry-based, GitHub Actions, and S3 backends. Registry cache is ideal for shared team environments, while GHA type integrates natively with GitHub runners. Choose based on infrastructure; avoid local cache in ephemeral CI to prevent redundant rebuilds and storage bloat.

BuildKit isolates build processes better than the legacy builder and supports rootless mode. However, secrets passed via ENV leak into layers. Always use --secret mounts or SSH agent forwarding in 2026 Dockerfiles to inject credentials safely without persisting sensitive data in the final image history.

Run docker buildx install or update via package manager.

Yes, rootless BuildKit runs entirely in user namespace using fuse-overlayfs or stargz snapshotter. Configure via dockerd-rootless.sh in 2026 setups. This prevents container breakout risks and allows unprivileged users to build images safely on shared hosts without sudo access or daemon restarts.

Default Docker driver lacks multi-platform support. Buildx creates a docker-container driver instance running BuildKit in an isolated container. This enables cross-compilation and advanced caching features unavailable in the host daemon, though it adds slight overhead compared to native builds on matching architectures.

Use --output=type=local,dest=./path to extract files directly from the build context without creating an image. This is useful for generating binaries, test reports, or static assets in CI pipelines. Specify multiple outputs or combine with registry pushes for flexible artifact delivery in 2026 workflows.

Yes, Compose V2 uses BuildKit natively.