Buildahは、デーモンを常駐させずにOCI形式のコンテナイメージを作るLinux向けのコマンドラインツールです。最新版は1.45.1(2026年9月15日公開)で、リポジトリは2026年にGitHubのcontainers/buildahからpodman-container-tools/buildahへ移っています。この記事では、buildah buildの既定値と、そのままだとHEALTHCHECKが消える落とし穴、Containerfileを使わない逐次ビルド、マルチアーキテクチャのpush、GitHub ActionsとKubernetesのPod内でrootlessビルドを回す設定までを、v1.45.1のマニュアルとソースに沿って解説します。
まとめ:Buildahの要点と使い分けの判断
Buildahは「イメージを作る」ことだけを担うツールです。Podmanがコンテナの実行と管理を、Buildahがビルドを担当し、podman buildも内部ではBuildahのGo APIを呼んでいます。デーモンが無いので、CIのジョブごとに独立したプロセスとしてビルドでき、一般ユーザーのままでも動きます。
buildah build(旧名buildah bud)は既存のDockerfileをそのまま読める。ただし既定のイメージ形式はOCIで、HEALTHCHECKとONBUILDは保存されない。必要なら--format dockerを付けるbuildah buildは中間レイヤーのキャッシュが既定で無効(podman buildは有効)。CIでキャッシュを使うなら--layersを明示し、ジョブ間でストレージを引き継がない場合はキャッシュの保存・復元も設定する- Kubernetesやコンテナの中でビルドするなら、公式イメージ
quay.io/buildah/stableがBUILDAH_ISOLATION=chrootを既定にしている。非特権で動かすには、この設定とsubuid・subgid、ストレージに加え、実行ユーザー、必要なケーパビリティ、ユーザー名前空間を許可するホスト側の設定を合わせる必要がある
採用が効くのは、共有CIやKubernetes上でビルドジョブにroot権限とDockerソケットを渡したくない場合と、RHEL・OpenShift・Podmanで道具をそろえたい場合です。開発者の手元がDocker Desktopで統一され、BuildKitのキャッシュ機能を使い込んでいるなら、置き換える理由は薄くなります。
Buildahの仕組み:デーモンを持たないビルドとrootless
fork-execモデルとPodmanとの役割分担
Dockerはdockerdという常駐デーモンにCLIが命令を送り、デーモン側でビルドします。BuildahのREADMEは自身を「シンプルなfork-execモデルで、デーモンとしては動かない」と説明しています。buildahコマンドを実行したプロセスがその場でレイヤーを組み立て、終わればプロセスごと消えます。デーモンが落ちて全ジョブが止まる、ソケット経由で他人のビルドに触れる、といった問題が構造上起きません。
Podmanとの関係もREADMEに明記されています。PodmanはDockerfileからのビルドにBuildahのGo APIを使い、Buildahとは独立してインストールできます。一方で「コンテナ」の意味が違い、Buildahの作業用コンテナはイメージに中身を足すための一時的なもので、Podmanの長時間動かすコンテナとは管理の仕方が違います。同じユーザーなら保存領域(containers storage)は共有されますが、通常のpodman psにBuildahの作業コンテナは出てこず、podman ps --externalで初めて表示されます。Podman側の操作はPodmanコマンド一覧と使い方にまとめています。
rootlessを支えるユーザー名前空間とsubuid・subgid
一般ユーザーでビルドできるのは、ユーザー名前空間でコンテナ内のUID 0をホストの自分のUIDに対応させ、さらに/etc/subuidと/etc/subgidに割り当てた範囲を追加のUID・GIDとして使うからです。ベースイメージのファイルは複数のUIDで所有されているので、この範囲が無いとレイヤーを正しく展開できません。
# 自分に割り当てられた範囲を確認(例:shichiza:100000:65536)
grep "^$(whoami):" /etc/subuid /etc/subgid
# 名前空間の中でUIDの対応表を確認する
buildah unshare cat /proc/self/uid_map
buildah unshareは、自分のUIDを0に見せた名前空間でコマンドを動かすサブコマンドです。マニュアルには、一般ユーザーのbuildah mountはbuildah unshareのセッション内でないと失敗すると書かれています。rootlessでの不調は、まずこの2つのファイルとuid_mapを見ると切り分けが早く進みます。ストレージはoverlayかVFSで、Podmanのrootless文書によると一般ユーザーがカーネルのoverlayfsを直接使えるのはLinux 5.12以降です。それより古いカーネルでは低速なfuse-overlayfsに切り替わります。
最新版1.45.1とディストリビューションの同梱版の差
上流の最新は1.45.1ですが、OSのパッケージで入る版はかなり古いことがあります。GitHub Actionsのubuntu-24.04ランナー(イメージ20260927.320.1)に入っているのは1.33.7、コンテナイメージquay.io/buildah/stableのlatestは1.43.4(2026年10月9日時点)です。マニュアルの新しいオプションが手元で通らないときは、buildah versionで版を確かめてから読むべきページの版を合わせてください。
# Fedora・RHEL系
sudo dnf -y install buildah
# Ubuntu 20.10以降・Debian
sudo apt-get -y update
sudo apt-get -y install buildah
buildah version
公式のインストール手順はLinuxディストリビューションだけを対象にしています。macOSとWindowsにはネイティブ版が無いので、WSL2やLinuxの仮想マシンの中で使います。
buildah buildでContainerfileからイメージを作る手順
buildah buildの基本構文と既定値
buildah buildは、ビルドコンテキストにあるContainerfileまたはDockerfileを読んでイメージを作ります。buildah budは同じコマンドの旧名で、今もエイリアスとして使えます。ファイルの書き方はDockerfileと同じなので、命令の基礎はDockerfileの書き方と主要命令がそのまま当てはまります。
FROM registry.access.redhat.com/ubi9/python-312
WORKDIR /opt/app-root/src
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8080
CMD ["python", "app.py"]
# カレントディレクトリのContainerfileでビルドし、キャッシュを有効にする
buildah build --layers -t localhost/myapp:1.0 .
# ファイル名を指定する場合
buildah build -f Containerfile.prod --layers -t localhost/myapp:1.0 .
buildah images
Dockerから移るときに差が出やすい既定値は次の3つです。
| 項目 | buildah build の既定 | 変更方法 |
|---|---|---|
| イメージ形式 | oci | –format docker / BUILDAH_FORMAT |
| 中間レイヤーのキャッシュ | 無効(false) | –layers / BUILDAH_LAYERS=true |
| RUNの隔離方式 | oci(一般ユーザーはrootless) | –isolation / BUILDAH_ISOLATION |
キャッシュの既定はpodman buildと逆です。Podmanのマニュアルでは--layersの既定はtrueで、同じContainerfileでもBuildahで直接ビルドすると毎回すべてのRUNをやり直します。CIでビルド時間が急に延びたら、まず--layersの付け忘れを疑ってください。
OCI形式の既定でHEALTHCHECKが消える落とし穴
Dockerから移ったときに最も気づきにくいのがこの点です。OCIのイメージ仕様には、HEALTHCHECKやONBUILDに当たる項目がありません。v1.45.1のソース(config.go)では、OCI形式で書き出すときにHEALTHCHECK・ONBUILD・DOMAINNAME・COMMENTを破棄し、SHELLはビルド中のRUNには使うものの保存しない、という警告を出します。
WARN[0000] HEALTHCHECK is not supported for OCI image format and will be ignored. Must use `docker` format
ビルド自体は成功するので、ログを流し見していると、Docker Composeのdepends_on: condition: service_healthyやDockerのヘルスチェックが動かなくなってから気づきます。ヘルスチェックをイメージに持たせる設計なら、--format dockerを付けるか、CIの環境変数でBUILDAH_FORMAT=dockerを設定します。Kubernetesはイメージ内のHEALTHCHECKを使わず、Podのprobeで死活を判定するので、Kubernetesだけで動かすならOCI形式のままで問題ありません。
buildah build --format docker --layers -t localhost/myapp:1.0 .
buildah inspect --format '{{.Docker.Config.Healthcheck}}' localhost/myapp:1.0
from・run・commitで組むContainerfileなしの逐次ビルド
シェルスクリプトで1ステップずつ組む手順
Buildahならではの使い方が、Containerfileを書かずにコマンドを並べてイメージを作る方法です。buildah fromで作業用コンテナを作り、buildah runで中のコマンドを実行し、buildah copyでファイルを入れ、buildah configで起動コマンドやポートを設定して、buildah commitでイメージに確定します。READMEのlighttpdの例を、ファイル投入を足して整理すると次のようになります。
#!/usr/bin/env bash
set -euo pipefail
ctr=$(buildah from registry.fedoraproject.org/fedora:latest)
trap 'buildah rm "$ctr" >/dev/null' EXIT
buildah run "$ctr" -- dnf install -y lighttpd
buildah run "$ctr" -- dnf clean all
buildah copy "$ctr" ./public /var/www/lighttpd
buildah config --port 80 \
--cmd "/usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf" \
--label "org.opencontainers.image.source=https://example.com/repo" \
"$ctr"
buildah commit "$ctr" localhost/lighttpd:1.0
Containerfileとの違いは、各ステップの間に普通のシェルの条件分岐やループ、外部コマンドの結果を挟める点です。たとえば環境ごとに入れるパッケージを変える、ビルド中に生成した設定ファイルをテンプレートから作る、といった処理をビルド引数の工夫なしで書けます。逆に、手順が直線的ならContainerfileのほうが他の道具でも読めるので、逐次ビルドは「分岐が要るときだけ」に限るのが無難です。
scratchとbuildah mountで最小イメージを作る方法
buildah from scratchは空のイメージから作業コンテナを作ります。buildah mountでそのルートファイルシステムをホスト側にマウントすれば、ホストのdnfで--installrootを指定して、パッケージマネージャーを含まないイメージを作れます。一般ユーザーで動かす場合、mountはbuildah unshareの中で実行する必要があります。
buildah unshare bash -c '
set -e
ctr=$(buildah from scratch)
mnt=$(buildah mount "$ctr")
dnf -y install --installroot="$mnt" --use-host-config --setopt install_weak_deps=False coreutils-single
dnf -y clean all --installroot="$mnt"
buildah config --cmd "/usr/bin/ls -l /" "$ctr"
buildah commit "$ctr" localhost/minimal:1.0
buildah umount "$ctr"
'
この例は、ホストのdnfコマンドがDNF5であり、ホストのリポジトリ設定を使ってイメージを作る構成を前提とします。別ディストリビューションのルートファイルシステムを作る場合は、対象のリポジトリ、リリース番号、署名鍵などを別途設定する必要があり、この例の対象外です。単にイメージを小さくしたいだけなら、マルチステージビルドのContainerfileで十分なことが多いでしょう。
buildah pushとマルチアーキテクチャのイメージ配信
buildah loginとpushでレジストリへ送る手順
作ったイメージはbuildah pushでレジストリへ送ります。宛先にはdocker://(レジストリ)、oci:(OCIレイアウトのディレクトリ)、dir:などの転送先を書けます。認証はbuildah loginで、CIでは--password-stdinを使ってパスワードをコマンドラインに残さないようにします。
echo "$REGISTRY_PASSWORD" | buildah login -u "$REGISTRY_USER" --password-stdin quay.io
buildah push localhost/myapp:1.0 docker://quay.io/example/myapp:1.0
push先をどこにするかは、アクセス制御と運用費に効きます。候補の比較はコンテナレジストリの種類と選び方を参照してください。
–platformと–manifestで複数アーキテクチャをまとめる方法
--platformは複数回指定するか、カンマ区切りで並べられます。マニュアルは、複数のプラットフォームを指定するときは--tagではなく--manifestを使うよう指示しています。できたマニフェストリストはbuildah manifest pushで送り、各アーキテクチャのイメージも一緒に送るかどうかを決める--allは既定でtrueです。
buildah build --platform linux/amd64,linux/arm64 --manifest localhost/myapp:1.0 .
buildah manifest inspect localhost/myapp:1.0
buildah manifest push localhost/myapp:1.0 docker://quay.io/example/myapp:1.0
ホストと違うアーキテクチャのRUNを実行するには、qemu-user-staticなどのエミュレーターをホストに入れておく必要があります。マニュアル自身が「可能ならネイティブのアーキテクチャでビルドするほうがよい」と書いているとおり、エミュレーションではコンパイルを伴うRUNが大幅に遅くなります。arm64のランナーを用意できるなら、アーキテクチャごとに別ジョブでビルドして最後にマニフェストを組むほうが速く終わります。
Podman・Docker(BuildKit)とのビルドの違い
3つとも同じContainerfileを読めますが、ビルドを動かす仕組みと既定値が違います。
| 観点 | buildah build | podman build | docker build(BuildKit) |
|---|---|---|---|
| 常駐デーモン | なし | なし | dockerd(BuildKit内蔵) |
| ビルドの実装 | Buildah本体 | BuildahのGo API | BuildKit |
| レイヤーキャッシュ既定 | 無効 | 有効 | 有効 |
| 既定のイメージ形式 | OCI | OCI | Docker互換 |
| Dockerfile命令に対応する専用CLI | あり | なし | なし |
| 常用コンテナの起動・管理 | 対象外 | 可能 | 可能 |
手元で動かしてビルドもするならPodmanで足ります。Buildahを直接使う理由は、逐次ビルドが要る場合と、実行機能を持たないビルド専用の道具をCIに置きたい場合です。DockerとPodmanの違いはPodmanとdockerの違いで詳しく扱っています。Buildahのリモートキャッシュは--cache-to・--cache-fromでレジストリのリポジトリを保存先に指定する方式です。GitHub Actionsのキャッシュやローカルディレクトリなど、レジストリ以外の保存先へキャッシュを書き出したい場合は、それらの保存先を備えるBuildKitと比べて決めてください。BuildKitの仕組みはBuildKitとはで解説しています。
CIとKubernetesでrootlessビルドを回す設定
GitHub Actionsのubuntu-24.04でBuildahを使う設定
GitHub Actionsのubuntu-24.04ランナーにはBuildah・Podman・Skopeoが最初から入っており、追加のセットアップなしで使えます。同じGitHubホストランナーでも、軽量なubuntu-slimにはコンテナ関連のツールが入っていない点はGitHub Actionsのubuntu-slimとはで整理しています。
name: build-image
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7
- name: Build
run: |
IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}:${GITHUB_SHA::7}
echo "IMAGE=$IMAGE" >> "$GITHUB_ENV"
buildah version
buildah build --layers --format docker -t "$IMAGE" .
- name: Push
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | buildah login -u "${{ github.actor }}" --password-stdin ghcr.io
buildah push "$IMAGE"
${GITHUB_REPOSITORY,,}はbashの小文字化で、GitHub Container Registryがリポジトリ名に大文字を受け付けないことへの対策です。ランナーのBuildahは1.33.7と古いので、新しい版で入ったオプションを使うと失敗します。ランナー同梱版より新しい版が必要なら、必要な版を含むイメージを選び、ジョブコンテナ内でのユーザー名前空間・ストレージ・権限の設定も確認してください。Docker前提でワークフローを組む場合の設計はGitHub ActionsでDockerイメージをビルドすると比べてください。
Kubernetes・OpenShiftのPod内でビルドする設定
Pod内でのビルドは、Dockerソケットのマウントや特権コンテナを避けたいときにBuildahが選ばれる典型的な場面です。公式イメージquay.io/buildah/stableのContainerfileでは、UID 1000相当のbuildユーザーとsubuid・subgidの範囲が用意され、ENV BUILDAH_ISOLATION=chrootでRUNの隔離方式がchrootに設定されています。コンテナの中ではOCIランタイム(runc)での入れ子の隔離が動かないことが多いため、この既定が要になります。
Buildahの公式チュートリアル5は、OpenShift 4.16とBuildah 1.36.0で動作確認したrootlessビルドの構成を載せています。公式イメージをそのまま使うのではなく、次のような派生イメージを作るのが出発点です。
# チュートリアルの検証版。新しいタグへ替える場合は自環境で動作を確かめる
FROM quay.io/buildah/stable:v1.36.0
RUN touch /etc/subgid /etc/subuid \
&& chmod g=u /etc/subgid /etc/subuid /etc/passwd \
&& echo build:10000:65536 > /etc/subuid \
&& echo build:10000:65536 > /etc/subgid
# rootlessではrunc既定の隔離が動かないためchrootを使う
RUN echo "export BUILDAH_ISOLATION=chroot" >> /home/build/.bashrc
# ストレージにoverlayを使う
RUN mkdir -p /home/build/.config/containers \
&& (echo '[storage]';echo 'driver = "overlay"') > /home/build/.config/containers/storage.conf
USER build
WORKDIR /home/build
CMD ["python3", "-m", "http.server"]
このイメージをクラスタ内のレジストリへpushし、ビルド専用のサービスアカウントbuildah-saを作ってoc adm policy add-scc-to-user anyuid -z buildah-saでanyuid SCCを割り当てます。チュートリアルの説明では、anyuidが標準のrestricted SCCに対して足すCAP_KILL・CAP_SETGID・CAP_SETUIDのうち、ビルド中に別UIDでコマンドを動かすのに必要なのはSETGIDとSETUIDの2つです。Pod側では、ストレージをemptyDirのボリュームに置くことでoverlayを使えるようにしています。
spec:
serviceAccount: buildah-sa
containers:
- name: buildah
image: image-registry.openshift-image-registry.svc:5000/image-build/buildah
securityContext:
capabilities:
drop:
- KILL
volumeMounts:
- name: containersstorage
mountPath: /home/build/.local/share/containers/storage
volumes:
- name: containersstorage
emptyDir:
medium: ""
上はチュートリアルのReplicationControllerからPodテンプレートの部分を抜き出したものです。公式イメージを加工せずに使い、fuse-overlayfsで動かす場合は、ホストの/dev/fuseをコンテナに渡す必要があります。イメージのREADMEにも、fuse: device not foundが出たらホストでmodprobe fuseを実行するよう書かれています。OpenShift以外のKubernetesクラスタでは、Pod Security Admissionのrestrictedプロファイルが全ケーパビリティの削除を求めるので、ビルド用の名前空間だけbaselineにする、ノード側でユーザー名前空間の作成を許可する、といった調整が別途必要です。Podを動かすランタイム側の仕組みはruncとはとCRI-Oとはで確認できます。
Buildahを採用すべき場面と見送るべき場面
Buildahが投資に見合うのは、ビルドの権限を絞ることに具体的な利益がある組織です。次のどれかに当てはまるなら導入を検討してください。
- 共有のCIサーバーやKubernetesクラスタで、ビルドジョブにDockerソケットやrootを渡している
- RHEL・OpenShift・Podmanが本番の基盤で、ビルドの道具も同じ系統にそろえたい
- 条件分岐や外部処理を挟むビルドがあり、Containerfileとビルド引数だけでは書きにくくなっている
反対に、開発者の端末がDocker Desktopで統一され、BuildKitのキャッシュやsecretマウントを使い込んでいる組織が「デーモンレスだから」という理由だけで乗り換えると、既定値の違い(キャッシュ無効・OCI形式)でビルドが遅くなり、ヘルスチェックが消えるといった事故を抱えます。手元はDocker、CIだけBuildahと混在させる場合は、イメージ形式の差を確認せずに移行すると、必要な設定が保存されないおそれがあります。移すならCIから始め、--layersと--formatを明示したうえで、できたイメージをbuildah inspectで旧イメージと比べてから広げてください。CIとコンテナ基盤の権限設計をまとめて見直す場合は、AWS・Google Cloud・Azureのインフラ構築でもご相談を受けています。
Buildahについてよくある質問
buildah budとbuildah buildは違うコマンドですか?
同じコマンドです。budは「build-using-dockerfile」の略で、現在の正式名はbuildah buildです。マニュアルの書式欄に両方が並んでおり、オプションも挙動も同じです。
Buildahで作ったイメージはDockerで動きますか?
動きます。既定のOCI形式もDockerで実行できます。ただしHEALTHCHECKやONBUILDはOCI形式では保存されないので、Dockerでヘルスチェックを使うなら--format dockerでビルドしてください。
BuildahとBuildKitはどちらを選べばよいですか?
Dockerを使い続けてキャッシュやsecretの機能を活かすならBuildKit、デーモンとroot権限を外したCIやPod内ビルドを作りたいならBuildahです。速さではなく、ビルドジョブにどこまで権限を渡せるかで決めるのが実務的です。
Kubernetesの中でBuildahを使うと特権コンテナが必要ですか?
必須ではありません。BUILDAH_ISOLATION=chroot、subuid・subgidの範囲、ストレージ用のボリュームに加え、非rootユーザーでの実行と、ユーザー名前空間・ストレージ操作を許可するホスト側の設定が必要です。ケーパビリティはCAP_SETUIDとCAP_SETGIDが要り、OpenShiftではanyuid SCCなどでこれを許可します。
macOSやWindowsでBuildahは使えますか?
ネイティブ版はありません。公式のインストール手順はLinuxディストリビューションのみです。WSL2やLinuxの仮想マシンにインストールするか、Podman Desktopの仮想マシンでpodman buildを使えば、同じBuildahのビルド処理を利用できます。