
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Running a privileged Docker daemon inside CI runners or Kubernetes pods remains one of the most persistent security risks in modern DevOps workflows. Buildah: Build OCI Images Without Docker solves this by providing a daemonless, rootless-capable tool that constructs standard Open Container Initiative images directly from userspace. This approach eliminates the attack surface of a long-running root process while maintaining full compatibility with existing registries and runtimes. For teams implementing DevSecOps practices, migrating to Buildah is often the critical step needed to secure the software supply chain without sacrificing developer velocity.
How does Buildah build OCI images without Docker work?
Unlike traditional container tools that rely on a client-server model, Buildah operates as a standalone command-line utility that manipulates filesystem layers and metadata directly. When you invoke a build command, Buildah creates a working container (a lightweight sandbox), executes your instructions against it, and commits the resulting filesystem changes into an OCI image layout. There is no socket communication, no REST API, and no persistent background service consuming resources when idle.
This architectural difference has profound implications for security and resource efficiency. In my experience managing multi-tenant Kubernetes clusters, removing the Docker daemon means a compromised build pod cannot pivot to control other containers on the node. The tool leverages Linux user namespaces to map the unprivileged build user to root inside the container namespace, allowing package installation and file permission changes without actual host root access. If you are familiar with Kubernetes RBAC security models, think of Buildah as applying least-privilege principles to the image build process itself.
How do you configure Buildah for rootless builds on Ubuntu?
Rootless mode is not enabled by default on all distributions, and misconfiguration here is the most common reason engineers abandon Buildah prematurely. You must configure subordinate UID/GID ranges and verify storage drivers before your first build.
Configure subordinate IDs and storage
- Install Buildah and required dependencies:
sudo apt update sudo apt install -y buildah uidmap slirp4netns fuse-overlayfs - Verify subordinate ID mappings exist for your user:
If these return empty, add mappings:grep "^$(whoami):" /etc/subuid grep "^$(whoami):" /etc/subgidsudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $(whoami) - Configure the storage driver for rootless operation by editing
~/.config/containers/storage.conf:[storage] driver = "overlay" [storage.options.overlay] mount_program = "/usr/bin/fuse-overlayfs" - Test the configuration with a simple scratch build:
buildah from scratch buildah run --isolation chroot scratch echo "Rootless works"
A common mistake I see in Nepal-based development teams setting up local VPS environments is skipping the fuse-overlayfs installation. Without it, Buildah falls back to the VFS driver, which copies entire layer trees instead of using copy-on-write. Build times increase tenfold, and disk usage explodes. Always verify overlay support before proceeding to production pipeline integration.
What is the best way to write a Buildah build script for CI/CD?
While Buildah can parse Dockerfiles via buildah bud, its native scripting interface unlocks capabilities impossible with declarative syntax alone. Native scripts let you conditionally install packages, inject secrets at build time without baking them into layers, and validate intermediate states programmatically.
Here is a production-grade native build script pattern I use for Go microservices. Note how secrets are mounted temporarily during compilation but never committed:
#!/bin/bash
set -euo pipefail
ctr=$(buildah from docker.io/library/golang:1.23-alpine)
mnt=$(buildah mount $ctr)
buildah run $ctr apk add --no-cache git ca-certificates
# Mount private repo credentials only during fetch
buildah run --secret=id=git_token,src=/run/secrets/git_token \
$ctr sh -c 'git clone https://x-access-token:$(cat /run/secrets/git_token)@github.com/org/private-lib.git /tmp/lib'
buildah copy $ctr ./go.mod ./go.sum /app/
buildah run --workingdir /app $ctr go mod download
buildah copy $ctr . /app/
buildah run --workingdir /app $ctr go build -ldflags="-s -w" -o /app/server
buildah config --entrypoint '["/app/server"]' $ctr
buildah config --port 8080 $ctr
buildah config --env GIN_MODE=release $ctr
buildah unmount $ctr
buildah commit --squash $ctr myregistry.io/app:v1.2.3
buildah rm $ctr The --squash flag collapses all intermediate layers into a single layer, reducing both image size and the forensic surface area for vulnerability scanners. For teams already practicing build pipeline automation, this script pattern slots directly into GitHub Actions or GitLab CI runners without requiring Docker-in-Docker sidecars.
How does Buildah compare to Docker and Kaniko for image building?
Choosing the right builder depends on your specific constraints around security, performance, and ecosystem compatibility. Each tool occupies a distinct niche in the container build landscape.
| Criteria | Buildah | Docker | Kaniko |
|---|---|---|---|
| Daemon Required | No | Yes (rootful or rootless) | No |
| Rootless Support | Native, mature | Possible, complex setup | Designed for rootless |
| Dockerfile Compatibility | Full (via buildah bud) | Native | Full |
| Scripting / Programmatic Builds | Excellent (native CLI) | Limited (multi-stage only) | None (declarative only) |
| Secret Handling | --secret mount (safe) | BuildKit secrets (safe) | ConfigMap/volume mounts |
| Layer Caching | Local + registry cache | Local + BuildKit cache | Registry-only cache |
| Best Use Case | CI/CD, K8s jobs, custom workflows | Developer workstations | GKE/EKS managed CI |
In practice, I recommend Buildah for any environment where you control the build infrastructure and need flexibility. Kaniko excels specifically in managed Kubernetes CI systems like Google Cloud Build where even user namespaces are restricted. Docker remains appropriate for local development where convenience outweighs security concerns, but should be replaced in automated pipelines.
How do you integrate Buildah into Kubernetes and CI pipelines securely?
Deploying Buildah in orchestrated environments requires attention to pod security standards and storage provisioning. The goal is maintaining rootless operation while ensuring builds complete reliably under resource constraints.
- Pod Security Standards: Apply the
restrictedPod Security Admission level. Buildah works within this constraint when user namespaces are enabled at the node level (userns=keep-id). - Storage Provisioning: Use ephemeral volumes or PVCs with
ReadWriteOnceaccess for the build workspace. Avoid hostPath mounts entirely — they break rootless isolation guarantees. - Registry Authentication: Mount registry credentials as projected secrets rather than embedding them in build scripts. Use workload identity (IRSA, Workload Identity Federation) where available to eliminate static credentials.
- Resource Limits: Set memory requests to at least 1GiB for Go/Rust builds. CPU limits cause throttling during compression phases; prefer requests-only scheduling for build pods.
- Caching Strategy: Enable registry-based layer caching with
--cache-fromand--cache-toflags. Local cache volumes help but don't survive pod rescheduling in autoscaling clusters.
For teams operating in Nepal or regions with limited bandwidth to international registries, consider deploying a local pull-through cache like Harbor or Zot. Buildah respects standard registry mirror configurations in /etc/containers/registries.conf, dramatically reducing build times when base images are cached locally. This mirrors the optimization strategies discussed in container registry selection guides, but with Buildah-specific configuration paths.
Secure Your Build Pipeline with Buildah Today
Migrating to Buildah: Build OCI Images Without Docker is a concrete step toward supply chain security that pays dividends immediately in reduced attack surface and improved compliance posture. Start by converting a single non-critical CI job to validate your rootless configuration, then expand systematically across your pipeline. The initial setup investment in subordinate IDs and storage drivers is modest compared to the operational risk of running privileged daemons in shared infrastructure. If your team needs guidance on securing container build workflows or achieving SOC 2 compliance for your deployment pipeline, reach out to discuss your specific requirements.