Docker Buildx

Docker Buildx is the Docker CLI plugin that drives BuildKit: it selects the builder instance a build runs on and targets several platforms in one build.

Docker Buildx is the Docker CLI plugin that drives BuildKit, the engine behind modern Docker builds. It selects which builder instance runs a build, and it can produce images for several platforms from one docker buildx build invocation.

Buildx is the client half of a build. BuildKit is the engine half, and the two halves can sit on different machines, which is the property that makes a builder outside the GitHub Actions job possible.

Definition

Buildx installs as a CLI plugin and adds one build command plus a set of builder management subcommands. Its own reference lives in the Docker Buildx CLI documentation, checked on 2026-08-13. Since Docker Engine 23.0 the plain docker build command is routed through Buildx as well, so most builds already pass through the plugin even when the command line never says buildx.

The plugin carries three responsibilities.

The first is builder management. docker buildx create defines a builder instance, docker buildx ls lists the instances a machine knows about, docker buildx use sets the default, and docker buildx inspect reports the endpoints behind one. A builder instance is a named handle to one or more BuildKit endpoints, and each endpoint is called a node.

The second is driver selection. The driver decides where BuildKit runs and therefore what a build is able to do. Docker documents four drivers in the builder drivers reference, checked on 2026-08-13.

DriverWhere BuildKit runsMulti-platform in one buildLayer cache after the job ends
dockerInside the local Docker daemonOne platform per invocationDiscarded with the daemon
docker-containerA container Buildx starts on the local daemonYes, through QEMU emulationDiscarded with the container
kubernetesPods in a clusterYes, one node per architectureHeld by the pod volumes
remoteAn existing BuildKit daemon reached over TCPYes, one node per architectureHeld on the remote host

The docker driver gains multi-platform output when the daemon runs the containerd image store, which is the one exception to the first row.

The third responsibility is the feature surface. Multi-platform output through --platform, external cache backends through --cache-from and --cache-to, named build contexts through --build-context, and direct output selection through --push and --load all arrive with Buildx. The cache backends are the flags that matter most inside a workflow, and the cache backend documentation lists them: type=registry writes cache into a registry repository, type=gha writes into the GitHub Actions cache service, type=local writes to a directory on the machine running the build, and type=inline embeds cache metadata in the pushed image.

Driver choice and cache backend answer the same question in two ways. A build whose engine disappears when the job ends has to export its cache somewhere durable and import it again on the next run. A build whose engine outlives the job keeps the cache on local disk where BuildKit wrote it.

Example

The default builder in a GitHub Actions job is the daemon inside that job, so every layer it writes disappears when the job finishes. This workflow instead creates a builder on the remote driver, points it at a BuildKit endpoint reached over mutual TLS, and runs the build there:

name: image
on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Write the builder TLS material
        run: |
          mkdir -p "$RUNNER_TEMP/buildkit"
          printf '%s' "${{ secrets.BUILDKIT_CA }}" > "$RUNNER_TEMP/buildkit/ca.pem"
          printf '%s' "${{ secrets.BUILDKIT_CERT }}" > "$RUNNER_TEMP/buildkit/cert.pem"
          printf '%s' "${{ secrets.BUILDKIT_KEY }}" > "$RUNNER_TEMP/buildkit/key.pem"

      - name: Point buildx at the remote builder
        run: |
          docker buildx create \
            --name remote-builder \
            --driver remote \
            --driver-opt cacert="$RUNNER_TEMP/buildkit/ca.pem" \
            --driver-opt cert="$RUNNER_TEMP/buildkit/cert.pem" \
            --driver-opt key="$RUNNER_TEMP/buildkit/key.pem" \
            --use \
            tcp://buildkit.internal.example.com:1234

      - name: Build and push
        run: |
          docker buildx build \
            --builder remote-builder \
            --platform linux/amd64,linux/arm64 \
            --tag ghcr.io/${{ github.repository }}:${{ github.sha }} \
            --push \
            .

docker buildx inspect remote-builder reports what the job is now attached to, and the node list is the part worth reading:

Name:   remote-builder
Driver: remote

Nodes:
Name:      remote-builder0
Endpoint:  tcp://buildkit.internal.example.com:1234
Status:    running
Platforms: linux/amd64, linux/amd64/v2, linux/arm64

Four things move off the runner once that builder is in use.

Part of the buildDefault builder in the jobBuilder on the remote driver
Build contextRead from the job's own diskStreamed from the job to the builder
Layer cacheWritten into the job's daemon, discarded at job endWritten on the builder host, read by the next run
Registry pushThe job's daemon pushes after the buildThe builder pushes directly under --push
Multi-platformQEMU emulation inside the jobOne node per architecture on the builder

The --push line is easy to miss. A multi-platform result cannot be loaded into the classic Docker image store, so --load fails and the image has to go to a registry, which the multi-platform build documentation states directly. Pushing from the builder also keeps the finished layers off the runner entirely, so the job transfers the build context up and the digest back.

A workflow that keeps the default builder still benefits from the same flags. Adding --cache-to type=gha,mode=max and --cache-from type=gha gives the throwaway engine a durable place to put its layers between runs, at the cost of an upload and a download on every job.

FAQ

Do I need to install Docker Buildx separately?

Usually no. Buildx ships with Docker Desktop and with current Docker Engine packages as the docker-buildx-plugin, and the GitHub-hosted runner images include it. Run docker buildx version to confirm the plugin is on the path before adding a setup step.

What is the difference between docker build and docker buildx build?

Since Docker Engine 23.0 both commands reach the same BuildKit engine. The buildx form adds the flags the legacy path lacked: --builder to choose a builder instance, --platform for multi-platform output, and --cache-from and --cache-to for external cache backends.

Why does a multi-platform build fail on the default builder?

The default builder uses the docker driver, which builds for one platform per invocation unless the daemon runs the containerd image store. Create a builder on the docker-container, kubernetes, or remote driver, then pass --platform with the architecture list.

Start with $10 in free credits

Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.