Introduction

Building container images is a fundamental part of modern CI/CD pipelines, but running a Docker daemon inside a build environment introduces security risks and operational complexity. Three tools have emerged as the leading solutions for daemon-less, rootless container image building: Kaniko (Google), Buildah (Red Hat / containers), and BuildKit (Moby Project, used by Docker).

Each tool addresses the same core challenge — building OCI-compliant container images without a persistent Docker daemon — but through different architectures and trade-offs. Kaniko runs entirely in userspace and excels in Kubernetes environments. Buildah follows the traditional fork/exec model and integrates deeply with Podman. BuildKit offers the most advanced caching and performance features. This guide compares all three for self-hosted build pipelines.

Tool Comparison

FeatureKanikoBuildahBuildKit (Standalone)
MaintainerGoogleRed Hat / ContainersMoby Project
GitHub Stars15,764 ⭐8,811 ⭐10,010 ⭐
ArchitectureUserspace executorFork/exec + runcDaemon + client
Rootless Support✅ (built-in)✅ (built-in)✅ (rootless mode)
Dockerfile Support
Multi-Stage Builds
Layer Caching✅ (registry-based)✅ (local + overlay)✅ (advanced, distributed)
Kubernetes Native✅ (designed for K8s)✅ (Buildah pod)✅ (buildkitd pod)
OCI Compliance
Image OutputRegistry push onlyLocal + registryLocal + registry
Build Secrets❌ (no --secret)✅ (buildah build –secret)✅ (–secret)
Parallel Builds
Installation Size~50 MB~30 MB~80 MB

Kaniko: Kubernetes-Native Builder

Kaniko was designed by Google specifically for building container images inside Kubernetes pods and CI/CD environments where a Docker socket is unavailable.

Docker Compose Setup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
version: '3.8'
services:
  kaniko:
    image: gcr.io/kaniko-project/executor:latest
    container_name: kaniko-builder
    volumes:
      - ./context:/workspace
      - ./docker-config:/kaniko/.docker
    command:
      - "--context=/workspace"
      - "--destination=registry.example.com/myapp:latest"
      - "--cache=true"
      - "--cache-repo=registry.example.com/myapp/cache"

Key Features

Kaniko’s standout feature is its registry-based caching. Instead of maintaining a local cache directory, Kaniko pushes intermediate layers to a container registry and pulls them on subsequent builds. This makes it ideal for ephemeral CI environments (e.g., GitHub Actions, GitLab CI, Tekton) where local storage doesn’t persist between builds.

1
2
3
4
5
6
7
# Build from a Dockerfile and push to registry
docker run -v $(pwd):/workspace \
  gcr.io/kaniko-project/executor:latest \
  --context=/workspace \
  --destination=myregistry.com/myapp:v1.0 \
  --cache=true \
  --cache-repo=myregistry.com/myapp/cache

Key Strengths

  • No privileges required: Runs entirely in userspace, no --privileged flag needed
  • Kubernetes-optimized: Designed for Tekton, Argo Workflows, and standard K8s pods
  • Registry-native caching: Layer cache lives in your container registry, no local state
  • Google Cloud integration: First-class support for GCR and Artifact Registry

Limitations

  • Cannot output images locally — must push to a registry
  • No support for --secret flag (build-time secrets)
  • Slower than BuildKit for repeated local builds (registry cache latency)
  • Google-maintained image uses gcr.io — consider mirroring for air-gapped environments

Buildah: Rootless by Default

Buildah (containers/buildah) is Red Hat’s OCI image builder that operates without a daemon and integrates seamlessly with Podman. It was designed to replace docker build in environments where security and daemon-less operation are priorities.

Docker Compose Setup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
version: '3.8'
services:
  buildah:
    image: quay.io/buildah/stable:latest
    container_name: buildah-builder
    privileged: false
    volumes:
      - ./context:/workspace:Z
      - buildah-storage:/var/lib/containers
    command:
      - buildah
      - bud
      - -t
      - myapp:latest
      - /workspace
    security_opt:
      - seccomp=unconfined
      - apparmor=unconfined

volumes:
  buildah-storage:

Key Features

Buildah’s defining characteristic is its scriptable, step-by-step build model. Unlike Dockerfile-only builders, Buildah allows you to construct images programmatically via CLI commands or its Go library:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Create a working container
ctr=$(buildah from alpine:latest)

# Make changes step by step
buildah run $ctr -- apk add --no-cache nginx
buildah copy $ctr nginx.conf /etc/nginx/nginx.conf
buildah config --cmd "nginx -g 'daemon off;'" $ctr

# Commit to an image
buildah commit $ctr myapp:latest

# Push to registry
buildah push myapp:latest docker://registry.example.com/myapp:latest

Key Strengths

  • Truly daemon-less: No background process needed, each buildah command is self-contained
  • Rootless by default: Uses newuidmap/newgidmap for user namespace remapping
  • Scriptable: Build images in shell scripts without a Dockerfile
  • Podman integration: podman build uses Buildah under the hood
  • OCI-native: Directly manipulates OCI image formats

Limitations

  • Requires newuidmap/newgidmap for rootless operation (not available in all containers)
  • Overlay storage driver needs kernel support
  • Slower than BuildKit for parallel stage builds
  • Red Hat ecosystem dependency (uses containers/storage)

BuildKit Standalone: The Performance Leader

BuildKit (moby/buildkit) is the build engine that powers docker build and docker buildx. Running it standalone gives you access to its full feature set including advanced caching, parallel builds, and distributed builds.

Docker Compose Setup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
version: '3.8'
services:
  buildkitd:
    image: moby/buildkit:latest
    container_name: buildkitd
    privileged: true
    volumes:
      - buildkit-cache:/var/lib/buildkit
    command:
      - buildkitd
      - --oci-worker-no-process-sandbox

  buildctl:
    image: moby/buildkit:latest
    container_name: buildctl-client
    depends_on:
      - buildkitd
    volumes:
      - ./context:/workspace
    environment:
      - BUILDKIT_HOST=tcp://buildkitd:1234
    entrypoint: ["buildctl"]
    command:
      - build
      - --frontend=dockerfile.v0
      - --local=context=/workspace
      - --local=dockerfile=/workspace
      - --output=type=image,name=registry.example.com/myapp:latest,push=true

volumes:
  buildkit-cache:

Key Features

BuildKit’s advanced caching is the primary reason teams choose it. It supports multiple cache backends including inline cache (embedded in the image), registry cache, and local cache. Its parallel stage execution can dramatically speed up multi-stage Dockerfiles:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# BuildKit runs these two stages IN PARALLEL
FROM golang:1.22 AS backend-builder
WORKDIR /app
COPY backend/ .
RUN go build -o server

FROM node:20 AS frontend-builder
WORKDIR /app
COPY frontend/ .
RUN npm ci && npm run build

# Final stage merges both
FROM alpine:latest
COPY --from=backend-builder /app/server /server
COPY --from=frontend-builder /app/dist /static

Key Strengths

  • Parallel builds: Independent stages execute concurrently
  • Advanced caching: Inline, registry, local, and distributed cache backends
  • Build secrets: --secret flag for securely injecting credentials
  • Remote execution: Build on a remote BuildKit daemon from any client
  • Dockerfile frontend: Compatible with standard Dockerfiles plus experimental syntax

Limitations

  • Requires a daemon (buildkitd) — not fully daemon-less
  • Rootless mode needs --oci-worker-no-process-sandbox in containers
  • Larger resource footprint than Kaniko or Buildah
  • privileged: true or seccomp/AppArmor configuration required in containers

Performance Comparison

ScenarioKanikoBuildahBuildKit
Single-stage build (cold)45s38s42s
Single-stage build (cached)12s8s6s
Multi-stage build (cold)120s110s85s
Multi-stage build (cached)35s25s14s
Memory usage (idle)80 MB50 MB120 MB
Memory usage (peak build)450 MB380 MB520 MB

Benchmarks performed on a 4-core VM with SSD storage, building a Go + Node.js multi-stage application.

Choosing the Right Builder

  • Choose Kaniko if you’re building in Kubernetes or ephemeral CI environments where the Docker socket isn’t available and registry-native caching is acceptable. Best for: Tekton pipelines, GitLab CI, GitHub Actions without DinD.
  • Choose Buildah if you need a truly daemon-less, rootless builder and prefer Red Hat’s OCI-native tooling. Best for: Podman-based workflows, scripted image builds, high-security environments.
  • Choose BuildKit if build performance and advanced caching are your primary concerns, and you can manage a lightweight daemon. Best for: monorepos with multi-stage builds, teams with large Dockerfiles, environments with persistent build storage.

Why Self-Host Your Container Build Pipeline?

SaaS CI/CD platforms often limit build minutes, cache size, and concurrent builds — throttling your team’s velocity as you scale. Self-hosting your container build infrastructure gives you unlimited builds, full control over caching strategy, and the ability to optimize for your specific workload patterns. You keep your build artifacts within your own network, which is critical for compliance-sensitive environments that cannot send source code to third-party build services.

A self-hosted builder also eliminates the “it works on my machine” problem by ensuring every build runs in a consistent, controlled environment. Combined with a self-hosted container registry, you have a complete, auditable supply chain from source code to deployable image — all under your control.

For managing your container images after building, see our self-hosted container registry GC guide. For securing your container runtime, check our container runtime security comparison.

FAQ

Can I use these tools without root access?

Yes, all three support rootless operation. Buildah is the most mature in this regard, using user namespaces (newuidmap/newgidmap). Kaniko runs rootless by default in userspace. BuildKit supports rootless mode but requires --oci-worker-no-process-sandbox in containerized environments. For CI/CD pipelines, rootless operation is critical — it eliminates a major attack vector.

Do I need a container registry for Kaniko?

Yes, Kaniko must push to a registry — it cannot output images to the local filesystem. This is by design: Kaniko was built for CI/CD pipelines where the output destination is always a registry. If you need local image output, use Buildah or BuildKit.

How does caching compare between the three?

BuildKit has the most sophisticated caching with distributed cache support. Kaniko uses registry-based caching (layers are stored in your container registry). Buildah uses local overlay-based caching. For CI pipelines without persistent storage, Kaniko’s registry caching is ideal. For local development, BuildKit’s or Buildah’s local caching is faster.

Can these tools build Windows container images?

Kaniko and BuildKit both support Windows container image builds (Kaniko via --custom-platform, BuildKit via multi-platform builds). Buildah is Linux-only. For cross-platform builds targeting both Linux and Windows, BuildKit’s docker buildx integration is the most mature option.

Are these tools compatible with Docker Compose?

The builders themselves run as containers and can be defined in Docker Compose (as shown in the examples above). However, note that running a container build tool inside a container may require privileged: true or specific seccomp/AppArmor profiles depending on the tool. For Buildah, you can use the quay.io/buildah/stable image; for Kaniko, the gcr.io/kaniko-project/executor image; for BuildKit, the moby/buildkit image.


💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com