# setup-buildx-action failed to initialize builder is buildx speaking

> setup-buildx-action failed to initialize builder comes from the buildx binary, not the action. The words after the colon are the whole diagnosis.

Source: https://latchkey.dev/learn/github-actions/setup-buildx-failed-to-initialize-builder  
Updated: 2026-09-21

setup-buildx-action failed to initialize builder is a sentence written by the `buildx` binary that the action shells out to, and it names a builder and a node rather than a cause. Everything diagnostic is in the third part of it, after the last colon, which is the error the driver returned when buildx tried to reach the node it had just registered.

## What this error means

A `docker/setup-buildx-action` step fails in one of two named groups, `Creating a new builder instance` or `Booting builder`, and the log carries a line of the form `failed to initialize builder <name> (<name>0): <error>`. The two identifiers are the builder group and the node inside it, and the node is simply the builder name with an index appended, so a doubled-looking suffix is expected rather than a corruption. The part worth reading is what follows the final colon: a daemon socket that cannot be reached, a request that timed out, or a driver that refused. The builder does not survive this, either, which changes what a retry means.

```Actions log, quoted from docker/docker-install#486, a run on docker/setup-buildx-action@v3
Creating a new builder instance
Error: ERROR: failed to initialize builder builder-05f95600-d266-4d50-a4fd-39a423e5d0e0 (builder-05f95600-d266-4d50-a4fd-39a423e5d0e00): Cannot connect to the Docker daemon at unix:///run/user/1000/docker.sock. Is the docker daemon running?
```

## Common causes

### There is no reachable Docker daemon on the runner

The largest group by a distance, and the one the message names directly when it does. Self-hosted runners without Docker, container-based runners with no socket mounted, and GitHub-hosted macOS images all land here. The error after the colon names the socket or pipe that was tried, which is the fastest confirmation available.

### The daemon is listening somewhere the client is not looking

Rootless installations put the socket under `/run/user/<uid>`, and a client configured for the system path finds nothing. The failure reads identically to having no daemon at all, so the endpoint in the message is the only thing that separates them.

### The node was registered but BuildKit could not be started

This is the boot group rather than the create group. The `docker-container` driver has to pull and run a BuildKit image, so a registry that is unreachable, a rate limit, or a host with no room for another container stops it after registration succeeded.

### The endpoint responded too slowly to be considered up

Buildx loads node data under a context, and a host that is reachable but very slow surfaces as a deadline rather than a refusal. In our experience this is mostly Docker Desktop and heavily loaded self-hosted machines, and it is the one case where a retry is a reasonable first move.

## How to fix it

### Read the endpoint out of the message before anything else

1. Find the text after the last colon on the `failed to initialize builder` line.
2. If it names a socket or pipe, compare that path with where your daemon actually listens.
3. If it names a deadline rather than a refusal, the host answered too slowly and the daemon is probably up.
4. Note which group failed, `Creating a new builder instance` or `Booting builder`, because they point in different directions.

### Run buildx jobs where a daemon exists

On GitHub-hosted runners that means Linux. A macOS job cannot start a `docker-container` builder because those images ship no daemon, so a Docker build that has drifted onto a macOS runner has to move rather than be configured around.

```.github/workflows/docker.yml (illustrative)
build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: docker/setup-buildx-action@v4
      - uses: docker/build-push-action@v7
        with:
          push: false
```

### Point the client at the socket the daemon is really on

For rootless setups, set the Docker host for the job so the client and the daemon agree. Doing it at job level rather than per step avoids the situation where the setup step succeeds and the build step looks elsewhere.

```.github/workflows/docker.yml (illustrative)
env:
      DOCKER_HOST: unix:///run/user/1000/docker.sock
```

### Do not go looking for the builder afterwards

1. Remember that buildx removed or restored the node group on the way out, so the name in the message no longer exists.
2. Do not add a cleanup step that deletes it; that step will fail on a name that is already gone.
3. If you want a fresh attempt, re-run the setup step rather than trying to repair an instance.

## How to prevent it

- Keep Docker builds on runners that are known to have a daemon, and assert that in the job rather than discovering it here.
- Pin the driver deliberately, so a change of runner does not silently change which driver is attempted.
- When self-hosting, make the socket path explicit for the job rather than relying on the client default.
- Report the group name along with the message, since create and boot failures have different causes.

## Whose words these are

The sentence is built in `Create`, in buildx's `builder/builder.go`. After registering the node, buildx loads the node group with data, and for any node whose own error is set it formats `failed to initialize builder %s (%s): %s` with the group name, the node name and that node error. Nothing in `docker/setup-buildx-action` contains those words.

The action's contribution is the wrapping, and it differs by version. On current `master` the create step throws `Failed to create builder <name>: ` followed by the buildx line, and the boot step throws `Failed to boot builder: `; the helper that extracts that line, `getErrorMessage` in `docker/actions-toolkit`, scans standard error backwards for the last line starting with `ERROR:` and strips that prefix. The log quoted above is from a v3 run, which threw the raw standard error instead, which is why it still shows the `ERROR:` prefix and no wrapper sentence.

| Fragment | Written by | Where |
| --- | --- | --- |
| `Failed to create builder <name>:` | docker/setup-buildx-action | `src/main.ts`, the create group |
| `Failed to boot builder:` | docker/setup-buildx-action | `src/main.ts`, the boot group |
| `failed to initialize builder <a> (<b>):` | docker/buildx | `builder/builder.go`, in `Create` |
| Everything after the last colon | The driver, or the Docker daemon | Whatever the node returned |

> Because `getErrorMessage` returns one line, a multi-line error from the driver is reduced to its first line in the action's message. The full text is still in the step log above the failure.

## Two steps can produce it, and they are not the same failure

The action creates the builder and boots it in separate groups. `Creating a new builder instance` runs `docker buildx create` with the driver, driver options and buildkitd flags it assembled; `Booting builder` runs `docker buildx inspect --bootstrap`, which is what actually starts the BuildKit container for the `docker-container` driver.

A failure in the first group means buildx could not even talk to the node it was registering, which is nearly always the Docker endpoint itself. A failure in the second means registration succeeded and starting BuildKit did not, which points at image pulls, resources or driver options rather than at the socket. Naming the group in your report is worth more than naming the message.

## The builder does not survive the failure

In the same branch that formats the message, buildx rolls the store back. If this invocation created the group it removes it, and if it modified an existing one it restores the previous state. Only if that rollback itself fails does it report something different, `could not rollback to previous state: ` with the second error.

This is the practical consequence people trip over: after this failure there is no half-created builder to inspect or to reuse. A second `setup-buildx-action` step, or a retry of the job, starts from nothing rather than from a broken instance, and `docker buildx ls` in a later step will not show the name from the message.

## What actually goes wrong behind the last colon

On a GitHub-hosted Linux runner the Docker daemon is running and this step is uneventful, so the failures cluster on everything else. A self-hosted or third-party runner with no daemon at all reports that it cannot dial the socket; a rootless setup reports a socket path under `/run/user/<uid>` that the client is not looking at; a Docker Desktop host reports a named pipe and a context deadline rather than a refusal.

macOS runners are the other reliable source, because GitHub-hosted macOS images do not run a Docker daemon. A `docker-container` builder cannot be created there at all, and no combination of driver options changes that; the job has to move to Linux or stop using the driver.

> The message names the endpoint it tried. Comparing that path against the one your daemon actually listens on settles the rootless and Desktop cases in a single line.

## Why there is no recorded run on this page

Every instance of this failure is a statement about a Docker endpoint that is not ours: a socket path a particular host chose, a daemon a particular runner does or does not run, a named pipe a desktop product owns. To record one we would have to take a working runner and disable its daemon, which produces a log that says exactly what the three quoted third-party reports already say, with our repository name on it. The format string and the rollback are read from buildx, the wrapping from the action, and the logs come from hosts that hit this without being asked to.

## FAQ

### Does docker/setup-buildx-action write "failed to initialize builder"?

No. Those words are formatted in `Create` in buildx's `builder/builder.go` and reach the log through the binary the action runs. The action contributes only the wrapper sentence around them, and older majors did not even do that, passing the binary's standard error through unchanged.

### Why does the builder name appear twice with an extra digit?

Because the message prints the builder group and then the node inside it, and the node name is the group name with an index appended. A group called `builder-abc` has a first node called `builder-abc0`, so the second identifier legitimately looks like the first with a digit on the end.

### Can I inspect the failed builder to debug it?

No. The same branch that writes the message rolls the store back, removing a builder this run created or restoring the previous state of one it modified. By the time the step fails the name is gone, and a later `docker buildx ls` will not list it.

### Why does this always fail on macOS runners?

GitHub-hosted macOS images do not run a Docker daemon, so there is no endpoint for the node to reach and the `docker-container` driver cannot be created. Driver options cannot work around a missing daemon; the job needs a Linux runner.

## References

- [docker/buildx: builder/builder.go, the Create branch that formats the message and rolls back](https://github.com/docker/buildx/blob/master/builder/builder.go)
- [docker/setup-buildx-action: src/main.ts, the create and boot groups and their wrappers](https://github.com/docker/setup-buildx-action/blob/master/src/main.ts)
- [docker/actions-toolkit: buildx.ts, getErrorMessage and the ERROR: line it returns](https://github.com/docker/actions-toolkit/blob/main/src/buildx/buildx.ts)
- [docker/docker-install#486: the runner log quoted here, with the socket path it tried](https://github.com/docker/docker-install/issues/486)
- [docker/buildx#2783: the same message with a context deadline instead of a refusal](https://github.com/docker/buildx/issues/2783)

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
