2026年9月8日
2026年9月8日
GitHub ActionsでDockerイメージをビルドしてGHCRに公開する方法
GitHub Actions を使うと、push やタグ付けのたびに Docker イメージを自動でビルドし、GitHub Container Registry(GHCR)などのレジストリへ公開できます。この記事では、Docker 公式のアクション(docker/setup-buildx-action、docker/login-action、docker/metadata-action、docker/build-push-action)を組み合わせ、自作テーマを同梱した WordPress イメージを例に、ビルド・タグ付け・キャッシュ・公開までを順に説明します。プルリクエストではビルドの確認だけ、main やタグでは公開、という使い分けもできるようになります。
全体の流れと使うアクション
ワークフローの中で行うことは次の4つです。
- Buildx を準備する(
docker/setup-buildx-action): BuildKit を使ったビルド環境を用意します。キャッシュやマルチプラットフォームビルドに必要です。 - レジストリにログインする(
docker/login-action): GHCR なら、ワークフローに自動で発行されるGITHUB_TOKENでログインできます。 - タグとラベルを決める(
docker/metadata-action): ブランチ名、Git タグ、コミットの SHA などからイメージのタグを自動で生成します。 - ビルドして公開する(
docker/build-push-action): Dockerfile からイメージを作り、レジストリへ push します。
各アクションのメジャーバージョン(例の @v3 や @v6 など)は更新されることがあるため、使う前に docker 組織の各リポジトリで最新の推奨バージョンと入力項目を確認してください(2026年10月現在の例として記載しています)。
例に使う Dockerfile
リポジトリのルートに、公式の WordPress イメージをもとに自作テーマを追加する Dockerfile を置きます。
FROM wordpress:php8.2-apache
# 自作テーマをイメージに含める
COPY --chown=www-data:www-data ./theme/ /usr/src/wordpress/wp-content/themes/my-theme/
公式の WordPress イメージは、コンテナ起動時に /usr/src/wordpress の内容をドキュメントルートへコピーする仕組みのため、ここに置いています。ただし、ドキュメントルートをボリュームで永続化していて、そこに WordPress がすでに存在する場合はこのコピーが行われないため、新しいイメージに差し替えてもテーマが更新されないことがあります。テーマのディレクトリだけをイメージ側から使う、デプロイ時に別途反映するなど、更新の反映方法は運用に合わせて決めてください。ベースイメージのタグ(PHP のバージョンなど)は Docker Hub の公式ページで現在提供されているものを確認します。
ワークフローの全体
.github/workflows/docker-build.yml を作ります。
name: Docker Build
on:
push:
branches: [ main ]
tags: [ 'v*.*.*' ]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Buildx をセットアップ
uses: docker/setup-buildx-action@v3
- name: GHCR にログイン
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: タグとラベルを生成
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=sha
- name: ビルドして公開
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
各設定の意味
**permissions の packages: write** は、GITHUB_TOKEN に GHCR へ push する権限を与える設定です。これがないとログインはできても push で権限エラーになります。ジョブ単位で指定しておくと、ほかのジョブに不要な権限を渡さずに済みます。
プルリクエストでは公開しない: push: ${{ github.event_name != 'pull_request' }} により、プルリクエストではビルドが通るかの確認だけを行い、レジストリには何も上げません。ログインのステップも同じ条件で飛ばしています。フォークからのプルリクエストでは書き込み権限のあるトークンが渡されないため、この分け方にしておくとワークフローが失敗しません。
**metadata-action のタグ**: 例の設定では、main への push で main、v1.2.3 のタグを push したときに 1.2.3、さらにコミットごとに sha- で始まる短い SHA のタグが付きます。SHA のタグは「どのコミットから作ったイメージか」をたどるのに役立ちます。latest の扱いなど、タグの細かな規則は metadata-action のドキュメントで確認できます。
イメージ名: GHCR のイメージ名は小文字である必要があります。リポジトリ名やユーザー名に大文字が含まれていると push で失敗することがあるため、そのような場合は images: に小文字の名前を直接書いてください。
**キャッシュ(type=gha)**: ランナーは毎回新しい環境で起動するため、何もしないと毎回すべてのレイヤーをビルドし直します。cache-from と cache-to に type=gha を指定すると、GitHub Actions のキャッシュ領域にレイヤーを保存し、次回のビルドで再利用します。mode=max は中間レイヤーも含めて保存する指定で、マルチステージビルドで効果が出やすい反面、キャッシュの容量を多く使います。キャッシュには容量の上限があり、古いものから削除されます。
キャッシュを効かせる Dockerfile の書き方
キャッシュはレイヤーの順番に左右されます。変更の少ない処理(OS パッケージや Composer の依存関係のインストール)を先に、頻繁に変わるファイルのコピーを後に書くと、テーマを1行直しただけのビルドが速くなります。
FROM composer:2 AS vendor
WORKDIR /app
COPY theme/composer.json theme/composer.lock ./
RUN composer install --no-dev --no-interaction --no-progress --optimize-autoloader
FROM wordpress:php8.2-apache
COPY --chown=www-data:www-data ./theme/ /usr/src/wordpress/wp-content/themes/my-theme/
COPY --from=vendor --chown=www-data:www-data /app/vendor/ /usr/src/wordpress/wp-content/themes/my-theme/vendor/
composer.json と composer.lock だけを先にコピーしているので、依存関係が変わらない限り composer install のレイヤーはキャッシュから再利用されます。また、ビルド用の Composer は最終イメージに含まれないため、イメージも小さくなります。
マルチプラットフォーム(arm64)対応
ARM の VPS や Apple シリコンの Mac でも同じイメージを使いたい場合は、docker/setup-qemu-action を Buildx の前に追加し、build-push-action に platforms: linux/amd64,linux/arm64 を指定します。QEMU によるエミュレーションでビルドするため、arm64 側のビルドは大幅に遅くなることがあります。GitHub が提供する arm64 のランナーを使ってプラットフォームごとにネイティブでビルドする方法もありますが、利用条件はプランや時期によって変わるので公式ドキュメントで確認してください。
秘密情報をイメージに入れない
ビルド中に非公開のリポジトリから依存関係を取得するなど、認証情報が必要になることがあります。このとき ARG や ENV でトークンを渡すと、イメージの履歴やメタデータに値が残り、イメージを取得した人に読まれるおそれがあります。build-push-action の secrets 入力で BuildKit のシークレットとして渡し、Dockerfile では RUN --mount=type=secret,id=... で一時的にだけ参照する方法を使ってください。同じ理由で、wp-config.php の認証用ソルトやデータベースのパスワードもイメージに含めず、実行時に環境変数やシークレット管理の仕組みから渡します。
失敗したときの確認ポイント
- **
denied: permission_deniedやunauthorized**:packages: writeの付け忘れが多い原因です。組織のリポジトリでは、組織やパッケージ側の設定で Actions からの書き込みが制限されている場合もあります。 - **
invalid reference format**: イメージ名やタグに使えない文字(大文字など)が含まれています。metadata-actionの出力をログで確認します。 - 毎回フルビルドになる:
cache-from/cache-toの指定漏れか、Dockerfile の早い段階でリポジトリ全体をCOPYしているためにレイヤーが毎回変わっていることが考えられます。.dockerignoreで.gitやnode_modulesを除外するのも効果があります。 - 公開したイメージが見つからない・取得できない: GHCR に初めて公開したパッケージは非公開になっていることがあります。公開範囲はパッケージの設定画面で確認します。
まとめ
- Docker 公式の4つのアクション(setup-buildx・login・metadata・build-push)を組み合わせると、ビルドから GHCR への公開までを自動化できます。
packages: writeを付け、プルリクエストではビルド確認だけにして公開しない構成にします。metadata-actionでブランチ・タグ・SHA からタグを付け、どのコミットのイメージかをたどれるようにします。type=ghaのキャッシュと、変更の少ない処理を先に書く Dockerfile でビルド時間を短縮します。- 認証情報は
ARG/ENVではなくビルドシークレットで渡し、イメージに残さないようにします。