Do Remote Docker Builders Support Multi Platform Builds?

Yes. Each platform gets its own builder instance joined into one Buildx instance, so amd64 and arm64 compile natively and each bills as a separate session.

Yes. The documented shape is one builder instance per platform, joined into a single Buildx instance, so the amd64 layers compile on an amd64 machine and the arm64 layers compile on an arm64 machine with no instruction translation anywhere in the build (Docker builders documentation, checked on 2026-08-13). A multi architecture build therefore opens one session per architecture against the same builder profile, and each session bills independently at that profile's per minute rate (pricing page, checked on 2026-08-13).

Answer

Two conditions have to hold. The builder profile needs both architectures enabled in the WarpBuild dashboard, and the build step needs platforms: linux/amd64,linux/arm64. With both in place the action assigns one builder instance per architecture and hands the buildx invocation a builder that carries both nodes (Docker builders documentation).

In GitHub Actions that is one step:

name: image

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: warp-ubuntu-latest-x64-4x
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4

      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push both platforms
        uses: Warpbuilds/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
          platforms: linux/amd64,linux/arm64
          profile-name: "multi-arch-builder"
          timeout: 600000

Warpbuilds/build-push-action@v6 is a drop-in replacement for docker/build-push-action@v6 and sets up the remote builders itself, so the docker/setup-buildx-action step comes out of the workflow when it was only there to create a builder. The api-key input is needed only when the job runs on a runner that is not a WarpBuild runner; the job above uses warp-ubuntu-latest-x64-4x, so the action authenticates from the runner. timeout is the wait in milliseconds for the builders to become ready and defaults to 10 minutes. Warpbuilds/bake-action@v6 takes the same profile-name input for workflows driven by a bake file (Docker builders documentation).

Profile sizes and the architectures each one can serve, from the same documentation, checked on 2026-08-13:

Builder profilePer minute per sessionArchitectures
16 vCPU, 32 GB RAM, 100GB disk$0.06amd64, arm64, multi
32 vCPU, 64 GB RAM, 200GB disk$0.12amd64, arm64, multi
64 vCPU, 128 GB RAM, 200GB disk$0.24amd64, arm64, multi
96 vCPU, 192 GB RAM, 600GB disk$0.36amd64 only
96 vCPU, 192 GB RAM, 2TB disk$0.52amd64 only
192 vCPU, 384 GB RAM, 600GB disk$0.72amd64 only
192 vCPU, 384 GB RAM, 2TB disk$0.88amd64 only

The last four rows are the constraint most teams meet first: arm64 and multi architecture profiles cap at 64 vCPU, so the 96 vCPU and 192 vCPU sizes are reachable only by an amd64-only profile. Remote Docker builders sit alongside snapshot runners, CI observability, an MCP server, and the Action Debugger in the WarpBuild product surface, and the runners underneath them cover Linux x64, Linux ARM64, macOS, and Windows (cloud runners documentation).

Detail

Joining the second builder yourself

Outside GitHub Actions, or inside a workflow that needs custom steps between assignment and build, the same shape is assembled by hand. A multi architecture profile returns more than one entry in builder_instances from the assign call, and each entry has its own host and its own TLS material. The first instance creates the buildx instance, the second is added to it with --append (Docker builders documentation):

docker buildx create --name "$BUILDER_NAME" \
  --node "$BUILDER_ID" \
  --driver remote \
  --driver-opt "cacert=$CERT_DIR/ca.pem" \
  --driver-opt "cert=$CERT_DIR/cert.pem" \
  --driver-opt "key=$CERT_DIR/key.pem" \
  --use \
  tcp://$HOST

docker buildx create --name "$BUILDER_NAME" \
  --append \
  --node "$BUILDER_ID_2" \
  --driver remote \
  --driver-opt "cacert=$CERT_DIR_2/ca.pem" \
  --driver-opt "cert=$CERT_DIR_2/cert.pem" \
  --driver-opt "key=$CERT_DIR_2/key.pem" \
  --use \
  tcp://$HOST_2

docker buildx build --builder "$BUILDER_NAME" \
  --platform linux/amd64,linux/arm64 \
  -t myimage:latest --push .

$BUILDER_ID_2 and its request id come from .builder_instances[1] of the assign response, the same way the first instance comes from .builder_instances[0]. Two operational details travel with this route. Poll each instance for status: ready before appending it, because the certificates and host are only present once the instance is up. And complete every request id through the builder session endpoint when the build finishes: billing runs until the assigned builders are terminated, so a script that exits without completing its sessions keeps paying for both nodes.

Pricing a two platform build

Billing is per session, measured from when the builder action starts until the job completes, and a multi architecture build opens one session per architecture, alive until the post-action steps are done. The arithmetic below holds the build stage at 6 minutes of wall clock and the GitHub Actions job at 7 minutes on warp-ubuntu-latest-x64-4x at $0.008 per minute, so the only variable is the profile size. Rates from the pricing page and the Docker builders documentation, checked on 2026-08-13.

Builder profileSessionsBuilder cost per buildRunner cost per buildTotal per build300 builds per month
16 vCPU, 32 GB22 x 6 x $0.06 = $0.72$0.056$0.776$232.80
32 vCPU, 64 GB22 x 6 x $0.12 = $1.44$0.056$1.496$448.80
64 vCPU, 128 GB22 x 6 x $0.24 = $2.88$0.056$2.936$880.80

Three things about that table are worth saying out loud. The runner and the builder are separate resources and both are billed, which is why the runner column is there at all. The session count is 2 rather than 1 because each architecture holds its own instance, so a second platform doubles the builder line while leaving the runner line alone. And the 6 minute assumption is yours to replace: time your own Dockerfile on one profile size, then multiply.

Concurrency changes the shape of this in your favor. Jobs that overlap on the same profile share a session, billed from the first job's start to the last job's completion, and the documented guidance is roughly 8 vCPU and 16 GB of memory per concurrent build job. A 32 vCPU multi architecture profile therefore carries about four concurrent builds per architecture at the same $0.12 per minute per session that one build pays.

The failure when a platform has no native instance

The common error is exec format error, and it means the builder profile does not have the correct architectures selected (Docker builders documentation). No aarch64 instance was assigned, so the arm64 layers have no machine that can execute them, and BuildKit fails at the first RUN step of that platform's stage. In the job log it looks like this:

 > [linux/arm64 4/9] RUN apt-get update && apt-get install -y build-essential:
0.213 exec /bin/sh: exec format error
------
failed to solve: process "/bin/sh -c apt-get update && apt-get install -y build-essential"
did not complete successfully: exit code: 255

The tell is the platform prefix on the failing step. [linux/amd64 ...] steps complete normally in the same log while every [linux/arm64 ...] step dies at its first command, which points at the builder profile rather than at the Dockerfile. The fix is to enable both architectures on the profile and rerun; no workflow change is needed if platforms was already set.

The workaround people reach for instead is docker/setup-qemu-action, which registers binfmt handlers so a single amd64 node executes the aarch64 stage under translation. That produces an image, and it moves the compile-heavy steps of the arm64 stage onto an interpreter, which Docker's own documentation names as the slowest of the three multi platform strategies it lists (Docker multi-platform builds, checked on 2026-08-13). It also introduces failures that read like Dockerfile bugs, most often Illegal instruction from libraries probing for CPU features the interpreter does not implement. Enabling arm64 on the profile removes the interpreter from the build entirely.

Cache behavior across the two nodes

Each builder profile keeps a persistent layer cache, so cache-to and cache-from are not required and can come out of the workflow. Two facts govern how that cache behaves on a multi platform build. The cache is shared but eventually consistent, so layers written by one concurrent build may not be visible to another build running at the same time, and they are available to later builds after synchronization. And the cache has a TTL of 10 days: a profile that goes unused for longer than that is reset automatically, and the next build after the reset is a cold one (Docker builders documentation).

For the full picture of when a single multi platform builder beats splitting the build across native runners, see multi platform Docker builds on native runners and the Docker builds on GitHub Actions hub.

Does one builder profile handle both amd64 and arm64?

Yes, when both architectures are enabled on the profile. Each architecture then runs on its own builder instance, so a two platform build opens one session per architecture on that profile and each session bills independently until the post-action steps finish (Docker builders documentation). Profile sizes and rates are on the multi platform Docker builders page.

What is the largest multi platform builder profile?

64 vCPU, 128 GB RAM, 200GB disk at $0.24 per minute per session. The 96 vCPU and 192 vCPU profiles are amd64 only, so arm64 and multi architecture profiles cap at 64 vCPU (Docker builders documentation, checked on 2026-08-13). The pricing page carries the same rates next to the per-minute rates for runners.

Why does the arm64 half fail with exec format error?

The builder profile does not have arm64 enabled, so no aarch64 instance is assigned and the arm64 layers have nowhere native to execute. Enable both architectures on the profile, then set platforms: linux/amd64,linux/arm64 on the build step. If you would rather build each architecture on a runner of that architecture and merge the results, how to push a manifest list from GitHub Actions covers that shape end to end.

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.