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.
| Driver | Where BuildKit runs | Multi-platform in one build | Layer cache after the job ends |
|---|---|---|---|
docker | Inside the local Docker daemon | One platform per invocation | Discarded with the daemon |
docker-container | A container Buildx starts on the local daemon | Yes, through QEMU emulation | Discarded with the container |
kubernetes | Pods in a cluster | Yes, one node per architecture | Held by the pod volumes |
remote | An existing BuildKit daemon reached over TCP | Yes, one node per architecture | Held 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/arm64Four things move off the runner once that builder is in use.
| Part of the build | Default builder in the job | Builder on the remote driver |
|---|---|---|
| Build context | Read from the job's own disk | Streamed from the job to the builder |
| Layer cache | Written into the job's daemon, discarded at job end | Written on the builder host, read by the next run |
| Registry push | The job's daemon pushes after the build | The builder pushes directly under --push |
| Multi-platform | QEMU emulation inside the job | One 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.
Related Terms
- BuildKit, the build engine that Buildx drives: the graph solver, the concurrency model, and the cache mounts that Buildx exposes through its flags.
- Do I need Buildx in GitHub Actions: the direct answer for a workflow that already runs
docker build, including when a setup step is redundant. - Remote Docker builders, sizes and configuration: vCPU, memory, and disk options for builders that run outside the GitHub Actions job.
- Docker Buildx CLI reference: the upstream command and flag list.
- WarpBuild Docker builder documentation: builder profiles, the actions that configure a builder, and the CLI flow that creates a
remotedriver instance. - WarpBuild caching documentation: cache action inputs, key and version matching, and the entry expiry window.
- WarpBuild pricing: per minute rates by runner type and by builder size.
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.