{
  "manifest_version": "1",
  "slug": "docker-builder",
  "title": "BuildKit",
  "headline": "Run a remote Docker builder with a warm BuildKit cache",
  "description": "Point docker buildx on laptops and CI at one big exe.dev VM running BuildKit over SSH: a shared warm cache, GC limits, amd64 and arm64.",
  "category": "dev-tools",
  "tags": [
    "remote docker builder",
    "buildkit",
    "buildx remote driver",
    "docker build cache",
    "remote build cache",
    "shared build cache",
    "multi-arch builds",
    "ci docker builds"
  ],
  "url": "https://exe.dev/deploy/docker-builder",
  "markdown_url": "https://exe-staging.dev/deploy/docker-builder.md",
  "upstream": {
    "website": "https://docs.docker.com/build/buildkit/",
    "source": "https://github.com/moby/buildkit",
    "docs": "https://docs.docker.com/build/builders/drivers/remote/",
    "license": "Apache-2.0"
  },
  "example_vm_name": "my-builder",
  "time": "15 minutes",
  "stack": [
    {
      "name": "BuildKit 0.34",
      "detail": "buildkitd from the official release, the buildkit systemd service, socket /run/buildkit/buildkitd.sock",
      "data": "/var/lib/buildkit"
    },
    {
      "name": "SSH",
      "detail": "docker buildx's remote driver runs `buildctl dial-stdio` over exe.dev's SSH; no port is opened"
    },
    {
      "name": "Garbage collection",
      "detail": "limits in /etc/buildkit/buildkitd.toml, at most 60% of the disk for cache and 20% always free"
    }
  ],
  "access": [
    {
      "url": "ssh://my-builder.exe.xyz",
      "who": "private",
      "what": "BuildKit, for docker buildx, over SSH with exe.dev keys (yours, your team's, CI's)"
    }
  ],
  "delivers": [
    "BuildKit running as the buildkit systemd service, started at boot",
    "A socket that only the buildkit group can use, with exedev in it",
    "Garbage collection that keeps the cache under 60% of the disk, leaves 20% free, and keeps cache mounts for two weeks",
    "Builds for linux/amd64 natively and linux/arm64 through BuildKit's own QEMU"
  ],
  "owner_runs_after": [
    "docker buildx create --name exe --driver remote ssh://my-builder.exe.xyz",
    "docker buildx inspect exe --bootstrap"
  ],
  "deployable": true,
  "deploy": {
    "new_vm_url": "https://exe-staging.dev/new?prompt=Set%20up%20BuildKit%20on%20this%20VM%20by%20following%20the%20exe.dev%20recipe%20at%20https%3A%2F%2Fexe-staging.dev%2Fdeploy%2Fdocker-builder.md%20from%20top%20to%20bottom.%0A%0AThe%20recipe%27s%20example%20VM%20name%20is%20my-builder.%20This%20VM%27s%20name%20is%20the%20output%20of%20%60hostname%60%3A%20use%20it%20wherever%20the%20recipe%20writes%20my-builder.%20Skip%20the%20step%20that%20creates%20the%20VM%3B%20you%20are%20on%20it.%0A%0AThis%20recipe%20needs%20a%20100GB%20disk%2C%20and%20a%20new%20VM%27s%20may%20be%20smaller.%20Before%20you%20install%20anything%2C%20run%20%60df%20-h%20%2F%60.%20If%20its%20size%20is%20under%2090G%2C%20stop%20and%20ask%20me%20to%20grow%20the%20disk%2C%20either%20with%20this%20link%3A%20https%3A%2F%2Fexe.dev%2Fsuggest%3Fcommand%3Dresize%2520my-builder%2520--disk%253D100GB%20or%2C%20on%20my%20computer%2C%20with%20%60ssh%20exe.dev%20resize%20my-builder%20--disk%3D100GB%60.%20When%20I%20say%20it%27s%20done%2C%20run%20%60sudo%20resize2fs%20%22%24%28findmnt%20-no%20SOURCE%20%2F%29%22%60%2C%20check%20%60df%20-h%20%2F%60%20again%2C%20and%20carry%20on.%0A%0AYou%20are%20done%20when%20all%20of%20these%20are%20true.%20Check%20each%20one%20yourself%3A%0A-%20BuildKit%20running%20as%20the%20buildkit%20systemd%20service%2C%20started%20at%20boot%0A-%20A%20socket%20that%20only%20the%20buildkit%20group%20can%20use%2C%20with%20exedev%20in%20it%0A-%20Garbage%20collection%20that%20keeps%20the%20cache%20under%2060%25%20of%20the%20disk%2C%20leaves%2020%25%20free%2C%20and%20keeps%20cache%20mounts%20for%20two%20weeks%0A-%20Builds%20for%20linux%2Famd64%20natively%20and%20linux%2Farm64%20through%20BuildKit%27s%20own%20QEMU%0A%0ARead%20the%20whole%20recipe%2C%20but%20run%20only%20%22Set%20up%20the%20builder%20by%20hand%22%2C%0Askipping%20VM%20creation%3A%20fetch%20and%20run%20builder-setup.sh.%20Everything%20about%0Adocker%20buildx%2C%20~%2F.ssh%2Fconfig%2C%20CI%2C%20and%20sharing%20runs%20on%20the%20owner%27s%0Acomputer%20or%20is%20the%20owner%27s%20choice.%20Don%27t%20install%20or%20configure%20Docker%2C%0Adon%27t%20open%20BuildKit%20on%20TCP%2C%20and%20don%27t%20add%20users%20or%20SSH%20keys%20unless%20the%0Aowner%20asks.%20Verify%3A%20buildkit.service%20is%20enabled%20and%20active%3B%0A%60sudo%20-iu%20exedev%20buildctl%20debug%20workers%60%20lists%20one%20worker%3B%20and%0A%60sudo%20buildctl%20debug%20workers%20-v%60%20shows%20the%20five%20GC%20rules.%20Finish%20with%0Athis%20VM%27s%20CPUs%2C%20memory%2C%20and%20free%20disk%20%28nproc%2C%20free%20-h%2C%20df%20-h%20%2F%29%2C%20the%0A%60ssh%20exe.dev%20resize%60%20command%20from%20%22Size%20it%22%20with%20this%20VM%27s%20name%2C%20and%20the%0Acommands%20for%20the%20owner%27s%20computer.%0A%0AFinish%20with%20the%20URLs%20I%20should%20open%20and%20these%20commands%2C%20which%20only%20I%20can%20run%2C%20from%20my%20own%20computer%20%28with%20this%20VM%27s%20name%20in%20them%29%3A%0A%20%20docker%20buildx%20create%20--name%20exe%20--driver%20remote%20ssh%3A%2F%2Fmy-builder.exe.xyz%0A%20%20docker%20buildx%20inspect%20exe%20--bootstrap%0A",
    "disk": "100GB",
    "terminal": "curl -fsSL https://exe-staging.dev/deploy/docker-builder/prompt.txt | ssh exe.dev new --disk=100GB --prompt=/dev/stdin",
    "prompt": "Set up BuildKit on this VM by following the exe.dev recipe at https://exe-staging.dev/deploy/docker-builder.md from top to bottom.\n\nThe recipe's example VM name is my-builder. This VM's name is the output of `hostname`: use it wherever the recipe writes my-builder. Skip the step that creates the VM; you are on it.\n\nThis recipe needs a 100GB disk, and a new VM's may be smaller. Before you install anything, run `df -h /`. If its size is under 90G, stop and ask me to grow the disk, either with this link: https://exe.dev/suggest?command=resize%20my-builder%20--disk%3D100GB or, on my computer, with `ssh exe.dev resize my-builder --disk=100GB`. When I say it's done, run `sudo resize2fs \"$(findmnt -no SOURCE /)\"`, check `df -h /` again, and carry on.\n\nYou are done when all of these are true. Check each one yourself:\n- BuildKit running as the buildkit systemd service, started at boot\n- A socket that only the buildkit group can use, with exedev in it\n- Garbage collection that keeps the cache under 60% of the disk, leaves 20% free, and keeps cache mounts for two weeks\n- Builds for linux/amd64 natively and linux/arm64 through BuildKit's own QEMU\n\nRead the whole recipe, but run only \"Set up the builder by hand\",\nskipping VM creation: fetch and run builder-setup.sh. Everything about\ndocker buildx, ~/.ssh/config, CI, and sharing runs on the owner's\ncomputer or is the owner's choice. Don't install or configure Docker,\ndon't open BuildKit on TCP, and don't add users or SSH keys unless the\nowner asks. Verify: buildkit.service is enabled and active;\n`sudo -iu exedev buildctl debug workers` lists one worker; and\n`sudo buildctl debug workers -v` shows the five GC rules. Finish with\nthis VM's CPUs, memory, and free disk (nproc, free -h, df -h /), the\n`ssh exe.dev resize` command from \"Size it\" with this VM's name, and the\ncommands for the owner's computer.\n\nFinish with the URLs I should open and these commands, which only I can run, from my own computer (with this VM's name in them):\n  docker buildx create --name exe --driver remote ssh://my-builder.exe.xyz\n  docker buildx inspect exe --bootstrap\n",
    "prompt_url": "https://exe-staging.dev/deploy/docker-builder/prompt.txt"
  },
  "updated": "2026-11-04",
  "verified": "2026-11-04",
  "related": [
    "docker-host",
    "github-actions",
    "gitea",
    "forgejo",
    "tangled",
    "sandbox"
  ],
  "alternatives": [
    "Docker Build Cloud",
    "Depot"
  ],
  "files": [
    "https://exe-staging.dev/deploy/docker-builder/builder-setup.sh"
  ]
}
