Build Daily

Tinley Park · October 29, 2026
toolMoby project (Docker, Inc. and community maintainers)applied

BuildKit

BuildKit is the build engine behind docker build, also usable on its own as the buildkitd daemon and the buildctl client. A frontend turns a Dockerfile (or another format) into an LLB graph, a concurrent solver runs only the steps whose inputs changed, and an exporter pushes the result to a registry, a tarball or a local directory. Apache 2.0 and free. It fits when images must be built in Kubernetes or CI without a Docker daemon, with a cache that survives between builds.

Updated October 17, 2026

BuildKit is the engine that turns a Dockerfile and a source tree into a container image. Docker Engine has used it for docker build by default since Engine 23.0, but it also runs on its own as a daemon (buildkitd) driven by a small client (buildctl). That standalone form is what makes it useful on a Kubernetes cluster: CI jobs can build and push images with no Docker daemon and no Docker Desktop. The repository README and Docker's BuildKit docs own the full option reference.

What it is

A build toolkit from the Moby project, written in Go and licensed Apache 2.0. The README describes it as "a toolkit for converting source code to build artifacts in an efficient, expressive and repeatable manner." Its main parts:

  • buildkitd, the daemon. It runs on Linux (and, experimentally, Windows containers since v0.13). It has two worker backends: the OCI worker (runc or crun, the default) and the containerd worker. It serves a gRPC API on /run/buildkit/buildkitd.sock by default, or on TCP.
  • buildctl, the client, for Linux, macOS and Windows.
  • The Dockerfile frontend, built into the daemon and also published as the docker/dockerfile image, so a build can pin its own Dockerfile syntax version.
  • Container images moby/buildkit:latest and moby/buildkit:rootless, plus master and master-rootless builds from the main branch.

Release line. The current release is v0.34.0 (7 October 2026). It ships the Dockerfile frontend v1.28.0, adds opt-in BoltDB compaction to shrink the daemon's internal databases, retries transient registry token failures, and lets buildkitd take its config path from the BUILDKITD_CONFIG environment variable. The previous patch, v0.33.1 (30 September 2026), fixed a set of security advisories, including two rated high: cache poisoning via unvalidated image layer DiffIDs, and a daemon panic when started with --cdi-disabled. Any install older than v0.33.1 should be upgraded.

BuildKit sits in the Docker build family next to Buildx (the docker buildx CLI plugin that drives BuildKit builders) and Docker Build Cloud. Buildx is a client; BuildKit is the engine underneath.

How it works

A build is a pipeline of four stages. The frontend reads the build definition and emits LLB (low-level build), a binary, content-addressable graph of operations: pull this image, copy these files, run this command. The solver walks that graph concurrently, runs independent steps in parallel, and skips any step whose inputs have the same checksum as a step already in the cache. The worker runs each step in a container through runc or containerd, with snapshots on overlayfs. The exporter writes the result: an image pushed to a registry, an OCI or Docker tarball, a local directory, or the containerd image store.

The cache is keyed on LLB, not on image layers alone. BuildKit tracks the checksum of each operation and of the files mounted into it, so a change to one source file invalidates only the steps that read it. The cache lives in the daemon's state directory (/var/lib/buildkit), and it can also be exported to a registry, a local directory, S3, Azure Blob or the GitHub Actions cache, so a fresh daemon on another host can reuse it.

How a BuildKit build runs from a CI job

Step 1 of 6. The workflow step runs buildctl build with the Dockerfile frontend, the context directory and an image output with push=true.

A first build fills the cache; a rebuild after a small source change reuses every step whose inputs did not change.

The daemon can run in three shapes on Kubernetes, all with manifests in the repository's examples/kubernetes: a long-lived Deployment + Service (many clients, cache stays warm), a StatefulSet with client-side consistent hashing (each project keeps landing on the same daemon and its cache), or a Job using buildctl-daemonless.sh, which starts a throwaway daemon inside the build container. Each comes in privileged, rootless and user-namespace variants; the maintainers recommend rootless.

When to use it

Reach for it when:

  • Images must be built inside Kubernetes or a CI runner with no Docker daemon available.
  • Build time matters and the same image is rebuilt often: a warm cache skips unchanged dependency installs.
  • The Dockerfile uses BuildKit-only features: RUN --mount=type=cache, --mount=type=secret, --mount=type=ssh, multi-stage parallelism, heredocs.
  • Builds need provenance or SBOM attestations, multi-platform output, or reproducible timestamps (SOURCE_DATE_EPOCH).
  • Several pipelines should share one build service rather than each running its own Docker-in-Docker.

Skip it when:

  • docker build or docker buildx on a laptop already does the job; that is BuildKit already, with less setup.
  • The cluster forbids both privileged pods and the unconfined seccomp/AppArmor profiles that rootless mode needs.
  • The project is Go-only and a no-Dockerfile builder such as ko fits better.

At a glance

Core concepts

  • LLB: the intermediate graph format every frontend compiles to. Content-addressable, so identical operations share cache.
  • Frontend: converts a build definition into LLB. dockerfile.v0 is built in; gateway.v0 runs any frontend image.
  • Solver: the concurrent scheduler that decides what to run and what to reuse.
  • Worker: OCI (runc) or containerd, the backend that executes steps.
  • Exporter: where the result goes: image, oci, docker, local, tar.
  • Cache exporter/importer: inline, registry, local, gha, s3, azblob. inline supports only mode=min; use registry for mode=max (all intermediate layers).
  • Garbage collection: per-worker policies (reservedSpace, maxUsedSpace, minFreeSpace) in buildkitd.toml.

Key facts

Item Value
Licence Apache 2.0
Current release v0.34.0 (Dockerfile frontend 1.28.0)
Daemon platforms Linux; Windows containers experimental
Client platforms Linux, macOS, Windows
Default socket unix:///run/buildkit/buildkitd.sock
Config file /etc/buildkit/buildkitd.toml (rootful), ~/.config/buildkit/buildkitd.toml (rootless)
Rootless overlayfs kernel 5.11+ (or Ubuntu kernel); fuse-overlayfs on 4.18+
Telemetry OpenTelemetry traces over OTLP from buildkitd

BuildKit vs other in-cluster builders

Aspect BuildKit Buildah ko
Input Dockerfile or any LLB frontend Dockerfile or shell scripted steps Go source, no Dockerfile
Daemon Yes, or daemonless wrapper No No
Shared build cache Local, registry, S3, GHA, Azure Local storage, registry layer cache Go build cache only
Rootless Yes Yes Needs no privileges
Best fit General image builds with heavy caching Red Hat / Podman toolchains Go services

How to integrate

Build and push from the client. Against a running daemon:

buildctl --addr tcp://buildkitd:1234 build \
  --frontend dockerfile.v0 \
  --local context=. \
  --local dockerfile=. \
  --output type=image,name=registry.example.com/app:${GIT_SHA},push=true \
  --export-cache type=registry,ref=registry.example.com/app:buildcache,mode=max \
  --import-cache type=registry,ref=registry.example.com/app:buildcache

Registry credentials come from $DOCKER_CONFIG/config.json (default ~/.docker). --metadata-file meta.json writes the pushed digest for the next pipeline step.

Daemon config for a plain-HTTP internal registry and a bounded cache (buildkitd.toml):

[worker.oci]
  gc = true
  reservedSpace = "10GB"
  maxUsedSpace = "60%"
  minFreeSpace = "20GB"

[registry."registry.example.com:5000"]
  http = true

For a single push without a config change, the image output also accepts registry.insecure=true.

Daemonless build as a Kubernetes Job or workflow step (trimmed from the upstream rootless example):

containers:
  - name: buildkit
    image: moby/buildkit:v0.34.0-rootless
    env:
      - name: BUILDKITD_FLAGS
        value: --oci-worker-no-process-sandbox
    command: [buildctl-daemonless.sh]
    args: [build, --frontend, dockerfile.v0,
           --local, context=/workspace, --local, dockerfile=/workspace,
           --output, "type=image,name=registry.example.com/app:latest,push=true"]
    securityContext:
      seccompProfile: {type: Unconfined}
      appArmorProfile: {type: Unconfined}
      runAsUser: 1000
      runAsGroup: 1000
    volumeMounts:
      - {name: workspace, mountPath: /workspace, readOnly: true}
      - {name: buildkitd, mountPath: /home/user/.local/share/buildkit}

A daemonless Job starts with a cold cache unless --import-cache points at a registry or the state volume is persistent. For a long-lived daemon, expose it on TCP with mTLS (create-certs.sh in the examples directory makes the certificates).

In the GL stack

BuildKit v0.34.0 runs as a buildkitd daemon on the management cluster, one of three K3s clusters that run as Lima VMs on a single Mac. It builds the container images for builddaily.io, paiddaily.io and the other products.

  • Who calls it. The Argo Workflows build templates drive it, with one workflow per product image. This replaced the earlier local builds in Docker Desktop.
  • Cache. The daemon keeps its layer cache between runs, so a rebuild after a small code change reuses the unchanged dependency layers.
  • Output. Finished images are pushed to the private CNCF Distribution registry on the same cluster, tagged by git SHA. Traffic to the registry is plain HTTP inside the platform.

Gotchas

  • Upgrade past v0.33.1. The 30 September 2026 advisories cover cache poisoning, unverified blob content and several daemon crashes from crafted LLB or frontends. A shared daemon that builds untrusted Dockerfiles is exposed to all of them.
  • TCP without mTLS is dangerous. The README warns that RUN containers can call the BuildKit API too, so an open TCP listener lets a build step drive the daemon. Use mTLS or keep the daemon on a pod-local socket.
  • Inline cache is min only. --export-cache type=inline stores cache for the final image layers only. Multi-stage builds lose their builder-stage cache unless a separate type=registry,mode=max cache ref is used.
  • Inline cache needs an import flag. It is not read unless --import-cache type=registry,ref=... is passed.
  • http = true and insecure = true are different. http is plain HTTP; insecure is HTTPS with a self-signed certificate. The docs say not to set both.
  • Rootless network mode is always network.host, and rootless overlayfs needs kernel 5.11+ (or an Ubuntu kernel); older kernels fall back to the slower fuse-overlayfs or native snapshotter.
  • The cache grows until GC caps it. Set maxUsedSpace and minFreeSpace deliberately on a node that also runs workloads; buildctl du -v shows usage and buildctl prune clears it.
  • Parallel exports to one local cache directory with reset=true gained a protection only in v0.34.0. On older daemons, avoid running concurrent local cache exports to the same directory.

Risks

  • Licence and stewardship. Apache 2.0 and developed in the open under the Moby project. Docker, Inc. also sells Docker Build Cloud on the same engine, so the roadmap follows Docker's build products.
  • Privilege. A build daemon runs arbitrary code from Dockerfiles. Rootful mode needs a privileged pod; rootless mode still needs unconfined seccomp and AppArmor. Isolate it on a cluster or node that holds no production secrets.
  • Security cadence. The batch of advisories fixed in v0.33.1 shows the attack surface is real. Track releases and pin image tags.
  • Lock-in is low. Output is standard OCI images, and Dockerfiles stay portable to Buildah, Podman or a hosted builder.

Alternatives

  • Buildah (Apache 2.0, Red Hat): daemonless, rootless image builds from Dockerfiles or scripts. Pick it in Podman and OpenShift toolchains.
  • ko (Apache 2.0): builds Go services into images straight from source with no Dockerfile or daemon. Pick it when every image is a Go binary.
  • Docker Buildx with a remote builder: the same engine behind a friendlier CLI. Pick it when developers already use docker buildx and want to point it at a shared builder.
  • Kaniko: the Google repository is archived. Existing pipelines should plan a move to BuildKit or Buildah.

Related