
September 09, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Buildah: Build OCI Images Without Docker solves a problem every DevOps engineer hits eventually. Your CI runner is Linux, your app ships as a container, but installing and running the Docker daemon on build agents is slow, privileged, and often blocked by policy. Podman and other daemonless tools changed that model. Buildah focuses on one job: take a base image, run commands, and emit a valid OCI-compliant image you can push to any registry. On production Laravel and WordPress deployments I maintain, that separation keeps build pipelines simple and servers free of Docker entirely.
buildah bud for Dockerfiles or buildah from plus run/commit for shell workflows, then buildah push to a registry—no Docker daemon required, rootless on supported Linux hosts.What is Buildah and why build OCI images without Docker?
Buildah is a Red Hat–backed CLI in the containers/buildah project. It creates and modifies container images on disk. The output follows the Open Container Initiative (OCI) image spec, so Podman, containerd, Kubernetes, and cloud registries accept it without conversion.
Docker bundles image building, runtime, networking, and volumes behind one daemon. That works on a developer laptop. On a shared CI node or a small VPS running Apache and PHP-FPM, a always-on daemon is overhead you do not need. Buildah never starts a long-lived service. Each command runs, writes layers, and exits.
In my experience maintaining Linux production servers, the wins are practical. Builds run rootless where policy allows. CI jobs need fewer privileges. You avoid Docker Desktop licensing questions on macOS build farms by moving image creation to Linux runners. The tool pairs naturally with Podman, which I covered in a separate Podman vs Docker migration guide.
Core commands you will use daily
buildah bud— build from a Dockerfile (Buildah Using Dockerfile).buildah from— start a working container from a base image tag.buildah run— execute commands inside that working container.buildah commit— freeze the working container into a new image.buildah push— upload the image to a registry.buildah rm— remove working containers and dangling images.
How do you install Buildah on Ubuntu or RHEL?
Buildah ships in most modern Linux repos. Ubuntu 22.04 and 24.04 include it. RHEL, Fedora, and CentOS Stream package it through the containers repository. Install it the same way you would any other container tooling on Ubuntu, but you do not enable dockerd afterward.
Ubuntu 24.04 quick install
sudo apt update
sudo apt install -y buildah podman fuse-overlayfs uidmap
buildah --version
buildah info Verify rootless mode before wiring CI. Run buildah info as your deploy user. Look for rootless: true in the output. If it says false, configure subuid and subgid maps. That step mirrors what you need for rootless Podman.
Rootless prerequisites
- Ensure
/etc/subuidand/etc/subgidcontain entries for your user. - Install
fuse-overlayfson hosts without native rootless overlay support. - Set
export BUILDAH_ISOLATION=chrootwhen fuse is unavailable in CI sandboxes. - Log out and back in after subuid changes so the kernel applies new mappings.
For teams that prefer scripted server setup, this fits alongside routine support and maintenance work on Ubuntu VPS hosts. I treat Buildah as part of the same toolchain as GitLab CI and Deployer, not a separate platform.
How do you build a container image with Buildah step by step?
Two paths exist. Most teams start with an existing Dockerfile because Laravel, Symfony, and Node frontends already have one. Advanced pipelines use imperative from/run/commit scripts when they need finer layer control than Dockerfile syntax allows.
Path A: Build from a Dockerfile
Assume a Laravel 13 app on PHP 8.3. Your Dockerfile lives at the project root. From the repo checkout:
cd /var/www/myapp
buildah bud -t registry.example.com/myapp:2026.09.1 -f Dockerfile .
buildah images Flags worth knowing:
--layers— cache intermediate layers between CI runs (default on).--target stage— stop at a named multi-stage target, same as Docker.--platform linux/amd64— cross-build when QEMU binfmt is registered.--build-arg APP_ENV=production— pass build-time variables.
This mirrors multi-stage Docker builds you may already use. A typical PHP production Dockerfile might use a Composer stage, copy vendor into a slim runtime stage, and run php artisan config:cache at build time. Buildah executes those instructions identically.
Path B: Imperative shell workflow
Imperative builds help when you prototype a base image or patch a vendor image without editing a Dockerfile. Example: PHP-FPM runtime with extensions:
container=$(buildah from docker.io/library/php:8.3-fpm-bookworm)
buildah run $container -- apt-get update
buildah run $container -- apt-get install -y libzip-dev
buildah run $container -- docker-php-ext-install pdo_mysql zip opcache
buildah config --label org.opencontainers.image.source=https://example.com/myapp $container
buildah commit $container myapp-php:8.3
buildah rm $container Each run creates a new layer. Group related commands to avoid image bloat. The same advice applies when you shrink images in Docker; see our guide on reducing container image size.
Example Dockerfile for a Laravel API
FROM php:8.3-fpm-bookworm AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& composer install --no-dev --prefer-dist --no-interaction
FROM php:8.3-fpm-bookworm
WORKDIR /var/www/html
COPY --from=vendor /app/vendor ./vendor
COPY . .
RUN docker-php-ext-install pdo_mysql opcache
CMD ["php-fpm"] Build and tag it:
buildah bud -t ghcr.io/myorg/laravel-api:$(git rev-parse --short HEAD) . Commit the built frontend assets in CI if your production server has no Node.js. That pattern matches how I deploy several Laravel sites with GitLab CI and Deployer 7. Asset builds run in the pipeline; the server only runs PHP-FPM.
How does Buildah compare to Docker and Podman for CI pipelines?
Buildah builds images. Podman runs containers. Docker does both through one daemon. Pick the tool that matches the job, not the brand on the slide deck.
| Criteria | Buildah | Podman | Docker Engine |
|---|---|---|---|
| Primary role | Image creation and modification | Run containers daemonlessly | Build + run + network + volumes |
| Background daemon | None | None | dockerd required |
| Rootless builds | Yes, with subuid maps | Yes | Rootless mode exists but less common in CI |
| Dockerfile support | buildah bud | podman build (Buildah backend) | docker build |
| Typical CI fit | Dedicated build stage | Build + smoke test run | All-in-one if policy allows |
| Multi-platform | With QEMU binfmt | Same | Buildx with builders |
For GitLab CI, a lean job might use Buildah to produce the image, then Podman to run a quick health check before push. That split keeps privileges minimal. If you already use Docker Buildx for multi-platform builds, Buildah offers a similar --platform flag once binfmt handlers are installed.
Google Cloud Build and other hosted runners often expose Docker. On your own Ubuntu runners, Buildah avoids socket mounting and docker group membership. That matters on shared EC2 hosts where I run multiple client sites. One compromised build job with Docker socket access is a host takeover. Buildah in rootless mode reduces that blast radius.
Sample GitLab CI job
build-image:
stage: build
tags: [linux, buildah]
script:
- buildah bud -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- buildah push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
rules:
- if: $CI_COMMIT_BRANCH == "main" Authenticate with buildah login using CI variables. Store registry credentials in GitLab masked variables, not in the repo. This aligns with broader build pipeline automation best practices and the overview in our build automation guide.
How do you push, scan, and sign images built with Buildah?
Building is half the job. Production pipelines scan for CVEs, sign images, and push to a registry you control. Buildah outputs standard OCI artifacts, so the same tools work here as with Docker.
Push to a private registry
buildah login registry.example.com -u deploy -p "$REGISTRY_TOKEN"
buildah push localhost/myapp:2026.09.1 docker://registry.example.com/myapp:2026.09.1 For a self-hosted registry on the same VPS, see our notes on how to self-host a container registry. The push syntax is identical whether the image was built with Docker or Buildah.
Scan with Trivy
Trivy reads OCI layouts and archive tarballs. After build:
buildah push localhost/myapp:2026.09.1 docker-archive:/tmp/myapp.tar
trivy image --severity HIGH,CRITICAL /tmp/myapp.tar Fail the pipeline on critical findings. That gate pairs well with the workflow in our container image scanning with Trivy article. For supply-chain hardening, add Cosign image signing after scan passes.
Run a smoke test with Podman
podman run --rm localhost/myapp:2026.09.1 php artisan --version Buildah created the image. Podman runs it. No Docker involved. On a booking platform like Adventure Third Pole Trek, a smoke test might hit php artisan route:list or a health endpoint before deploy.
Storage cleanup on small VPS disks
Buildah stores layers under ~/.local/share/containers for rootless users. On a 40 GB VPS hosting PHP apps and MySQL, disk fills fast if CI never prunes. Schedule weekly cleanup:
buildah rm --all --force
buildah rmi --all --force Monitor free space the same way you would for database dumps. A full disk breaks deploys and container resource limits will not help if the host cannot write layers.
How do you use Buildah with Laravel, WordPress, and enterprise deploys?
Most of my client work still deploys as Git checkouts on PHP-FPM, not Kubernetes. Containers enter when a team wants identical staging and production, or when they move APIs to cloud run services. Buildah fits that transition because you can containerize without adopting Docker on the server.
For a Laravel 12 or 13 app, containerize the PHP-FPM side first. Keep Nginx on the host or in a separate container. Mount .env at runtime, not bake time. Never copy production secrets into an image layer. Use enterprise application development patterns: environment-specific config via orchestrator secrets or host-mounted files.
WordPress containers are trickier. Uploads and wp-content should live on persistent volumes. Buildah can produce a image with core PHP and extensions. Mount the database and media separately. For WooCommerce shops like florist sites in my portfolio, image builds focus on PHP version parity, not the whole media library.
When a team outgrows single-host Podman, the same OCI images deploy to Kubernetes. The build step does not change. Only the runtime target does. That is the point of the OCI standard: build once, run anywhere.
If you need a JSON manifest for your pipeline config, validate it with our JSON formatter tool before committing CI YAML. Small syntax errors waste long build cycles.
Key Takeaways
- Buildah builds OCI images without a Docker daemon—ideal for rootless CI on Ubuntu and RHEL runners.
- Use
buildah budfor existing Dockerfiles; usefrom/run/commitwhen you need scriptable layer control. - Pair Buildah with Podman for smoke tests, Trivy for scans, and Cosign for signing before registry push.
- Configure subuid/subgid and fuse-overlayfs before relying on rootless builds in production pipelines.
- Prune local container storage regularly on small VPS hosts to avoid failed builds from full disks.
- Keep secrets out of image layers; inject
.envand credentials at deploy time, not atbuildah budtime.
People Also Ask
Can Buildah replace Docker completely?
Buildah replaces Docker for image creation. You still need a runtime like Podman or containerd to run containers. Many teams drop Docker entirely by pairing Buildah plus Podman on Linux. Developers on macOS often keep Docker Desktop locally but build release images on Linux CI with Buildah.
Does Buildah support Docker Compose?
Buildah does not run Compose files. Use podman-compose or Podman’s native compose support to orchestrate multi-container stacks. Buildah handles the image half; Compose handles service wiring, networks, and volumes at runtime.
Is Buildah production-ready for GitLab CI and Jenkins?
Yes. Both platforms support shell-based build steps. Install Buildah on the runner, avoid mounting docker.sock, and push to GHCR, GitLab Registry, ECR, or a private registry. Red Hat and the CNCF ecosystem use this stack in production today.
How is Buildah different from Kaniko or Google Cloud Build?
Kaniko also builds without a daemon, mainly inside Kubernetes. Google Cloud Build is a hosted service. Buildah is a local CLI you control on any Linux runner. Choose Buildah when you self-host CI on a VPS or bare metal and want no daemon dependency.
Ship OCI images on your terms
Buildah: Build OCI Images Without Docker gives you a straightforward path off the Docker daemon without leaving the Dockerfile ecosystem you already know. Install it on your Linux runner, wire buildah bud and buildah push into GitLab CI, scan before deploy, and run containers with Podman on the host. That stack matches how I deploy and maintain production apps for clients who need reliable builds on budget-friendly VPS hardware in Nepal and abroad.
If you want help containerizing a Laravel app, hardening CI, or migrating off Docker on Ubuntu servers, contact us to discuss your pipeline. For related reading, explore Google Cloud Build automation, Packer for golden machine images, and more about how I work with PHP, Linux, and deployment automation.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

