2026年8月24日
2026年8月24日
DockerfileのONBUILD命令の使い方|仕組み・使いどころと、使う前に知っておきたい注意点
ONBUILD は、そのイメージを別のDockerfileの FROM に指定してビルドしたときに初めて実行される命令を「予約」しておく仕組みです。社内共通のベースイメージを作り、各プロジェクトのDockerfileを FROM の1行だけで済ませたい、という場面で使われます。一方で、子イメージの側からは何が実行されるか見えにくいため、公式イメージでも ONBUILD を使った派生版は廃止されてきた経緯があります。この記事では、ONBUILD の動き方、書き方の例、実行内容の確かめ方、そしてマルチステージビルドなどの代わりの方法と比べた使いどころを説明します。
内容はBuildKitを使う現在のDocker(2026年10月現在)を前提にしています。
ONBUILDの動き方
通常のDockerfileの命令は、そのイメージのビルド時に実行されます。ONBUILD を付けた命令は、ビルド時には実行されず、「トリガー」としてイメージの設定に保存されるだけです。
# 親イメージ(company/php-app-base)の Dockerfile
FROM php:8.3-cli
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app
ONBUILD COPY composer.json composer.lock ./
ONBUILD RUN composer install --no-dev --no-interaction --no-scripts
ONBUILD COPY . .
このイメージを使う子のDockerfileは次のようになります。
FROM company/php-app-base:1.0
CMD ["php", "bin/console"]
子イメージをビルドすると、FROM の直後に、親で予約した3つの命令が書いた順に実行されます。このとき COPY が参照するのは、子のビルドコンテキスト(子のDockerfileがあるプロジェクト)です。つまり、親は「プロジェクトの構成はこうなっているはず」という前提を子に押し付けることになります。
押さえておくルール
- トリガーが実行されるのは直接の子のビルドだけです。子イメージをさらにベースにした孫のビルドでは、親のトリガーは実行されません。
ONBUILD ONBUILD ...のような入れ子は書けません。ONBUILD FROMのように、FROMをトリガーにすることもできません。- トリガーのどれかが失敗すると、子のビルドはそこで止まります。
予約されている内容を確かめる
FROM に指定するイメージが ONBUILD を持っているかは、ビルドする前に確認できます。
docker pull company/php-app-base:1.0
docker inspect --format '{{json .Config.OnBuild}}' company/php-app-base:1.0
null や空の配列なら、トリガーはありません。中身が表示された場合は、その命令が自分のプロジェクトで問題なく動くかを読んでから使います。外部のベースイメージを使うときにこの確認を習慣にしておくと、「FROM しただけなのに知らないファイルがコピーされた」「依存パッケージのインストールが勝手に走った」といった事態を防げます。
ビルドログにも、トリガーとして実行された命令が表示されます。表示のされ方はビルダーやバージョンによって異なります。
使いどころ
ONBUILD が役に立つのは、次の条件がそろったときです。
- 同じ構成のプロジェクトが多数ある(例:同じ構成の社内向けPHPアプリやWordPressテーマのリポジトリが何十個もある)。
- プロジェクトの構成ルール(
composer.jsonの位置、ソースの置き場所)がチーム内で決まっている。 - 各プロジェクトのDockerfileを極力短くし、ビルド手順の変更をベースイメージ側で一括して反映したい。
この場合、ベースイメージのタグを上げるだけで、各プロジェクトのビルド手順をそろえて変えられます。
注意点と、使わない方がよい場面
子のDockerfileを読んでも動きが分からない
一番の弱点は見通しの悪さです。子のDockerfileには FROM しか書かれていないため、初めて見た人は何がコピーされ、何がインストールされるのか分かりません。ビルドが失敗したときも、原因がベースイメージ側にあることに気づくまで時間がかかります。ベースイメージのREADMEに、トリガーの内容と、子のプロジェクトに求める構成を書いておくことが必須です。
キャッシュとビルドコンテキスト
ONBUILD COPY . . は子のビルドコンテキストを丸ごとコピーします。子のプロジェクトに .dockerignore がないと、.git や .env までイメージに入ってしまいます。ベースイメージを配る側は、.dockerignore の用意を子のプロジェクトのルールとして明記しておきます。
また、親の側で COPY の順番を工夫しておかないと、ソースを1行変えただけで依存パッケージのインストールからやり直しになります。上の例で composer.json と composer.lock を先にコピーしているのはこのためです。
バージョンを固定する
子のDockerfileで FROM company/php-app-base:latest のようにタグを固定しないと、ベースイメージの更新によってトリガーの内容が変わり、ある日突然ビルドが通らなくなることがあります。1.0 のようなタグか、イメージのダイジェストで固定し、更新は意図して行います。
ほかのビルドツールでの扱い
ONBUILD はDockerのイメージ設定にある項目で、ツールや保存形式によっては扱いが異なります。たとえばBuildahなどでOCI形式のイメージを作ると、ONBUILD が保存されない場合があります。DockerとPodmanなど複数のツールを併用するチームでは、実際に子のビルドで動くかを試してから採用します。
代わりの方法
多くの場合、ONBUILD を使わなくても同じ目的は達成できます。
- マルチステージビルド:ビルド手順を子のDockerfileにそのまま書き、共通部分はベースイメージの「ツール入りの土台」にとどめる。手順が子のDockerfileで読めるため、見通しが良くなります。
- Dockerfileのテンプレート化:共通のDockerfileをリポジトリのテンプレートや社内のスキャフォールドで配る。各プロジェクトで差分が見えます。
ARGによるパラメータ化:PHPのバージョンなど、変わる部分だけを--build-argで切り替える。docker buildx bake:複数のイメージのビルド設定をまとめて定義し、共通の設定を一か所で管理する。
公式イメージでも、かつて用意されていた -onbuild 付きのタグは廃止され、利用者が自分でDockerfileを書く形が推奨されています。新しく設計するなら、まずは上の方法で足りないかを検討し、それでも「多数のプロジェクトでビルド手順をそろえたい」という理由がはっきりしている場合に ONBUILD を選ぶのがよいでしょう。
導入するときの手順
- 子のプロジェクトに求める構成(ファイルの位置、
.dockerignore)を決め、READMEに書く。 - ベースイメージを作り、タグを付けて社内レジストリに置く。
- 代表的なプロジェクトで子のビルドを試し、トリガーがすべて通ることと、イメージに不要なファイルが入っていないこと(
docker run --rm <イメージ> ls -la /appなど)を確認する。 - 各プロジェクトでタグを固定して
FROMする。ベースイメージを更新するときは、新しいタグを作ってから順に切り替える。
まとめ
ONBUILDは、そのイメージをFROMにした子のビルドで、FROMの直後に実行される命令を予約する。- トリガーは直接の子だけで実行され、孫には引き継がれない。
ONBUILD ONBUILDやONBUILD FROMは書けない。 - 使う前に
docker inspect --format '{{json .Config.OnBuild}}'で予約内容を確認する。 - 子から動きが見えにくいため、READMEでの説明、
.dockerignore、タグの固定が欠かせない。 - 多くの場合はマルチステージビルドやテンプレート化で代用でき、そのほうが見通しが良い。