サマリ
既存の WordPress サイトを、AI エージェントを利用しつつ安全に修正できるように、下記の構成を組んでみました。
構成は 自作テーマ・プラグイン git 管理 / wp-content 全体は repo に置くが git 外 / WordPress 本体は公式 Docker image に任せる + UID 整合 + dev container 内 Claude Code です。本記事はその 構成全体 をまとめます。
設計判断・思想は別記事(末尾の関連記事)に書きました。
- Docker の bind mount は
wp-contentだけ、WordPress 本体は公式 WordPress Docker image を利用する -
wp-config.phpと.htaccessは read-only bind で永続化 -
www-dataの UID を build 時にホスト UID へ揃え、パーミッションを統一する -
.vscode/settings.jsonのintelephense.environment.includePathsにコンテナ内パスを渡し、ホストに WordPress 本体のソースを置かずにエディタのコードサポートを使用可能に - Dockerfile に
INSTALL_CLAUDEbuild arg を入れて、dev container 内に Claude Code を同梱
実現したこと
構成を組んでみて、結果として以下が手に入りました。
-
テーマ・プラグインの「自分が書いたコード」だけ追跡される —
git statusに出るのはwp-content/themes/mytheme/配下だけ。本体や uploads は除外し、純粋に自分のコードだけが履歴に残ります。 -
WordPress 本体のバージョン更新が image タグの変更だけで済む —
docker-compose.ymlのimage: wordpress:6.9-php8.4-apacheのタグを切り替えて、コンテナと匿名ボリュームを破棄するだけ。 -
wp-content の中身を本番から持ってこれる —
.gitignoreがリポジトリルートに集約してあるので、本番サーバからwp-content/を持ってきて差し替えても ignore ルールが上書きで消えない。 -
VS Code から WordPress 本体関数の実装に飛べる — ホスト側のリポ(ワークスペース)には WordPress コアが無いのに、エディタからは見えている状態。Intelephense にコンテナ内
/var/www/html/wp-admin,/var/www/html/wp-includesをincludePathsとして渡している。 -
UID 整合で bind mount の権限事故が起きない —
www-dataの UID を Docker ホストの UID に揃えるよう build しているので、bind mount したファイルの所有権が一致。Apache / Claude Code / VS Code どれがファイルを書いても権限の食い違いが起きない -
Claude Code を container 内で動かせる —
INSTALL_CLAUDE=trueの build arg で Dockerfile がclaudeを container 内にインストール。.claude/は named volume で永続化。Docker 内の隔離環境で安全に AI エージェントを利用できる。
構成のあらまし
詳細は派生記事に記載しましたので、要点のみです。
ディレクトリ構成
repo-root/
├── .gitignore ← 自作テーマ・プラグインのみ追跡
├── .vscode/
│ └── settings.json ← Intelephense の設定
├── docker-compose.yml
├── Dockerfile ← UID 整合 + Claude Code 同梱
├── docker/
│ └── wp/
│ ├── wp-config.php ← bind 用ローカル wp-config (Read Only)
│ └── .htaccess ← bind 用ローカル .htaccess (Read Only)
├── wordpress/ ← repo 内には置くが git 管理対象は wp-content/themes/mytheme/ のみ
│ └── wp-content/
│ ├── themes/
│ │ └── mytheme/ ← 自作テーマ(git 管理)
│ ├── plugins/ ← 本番から コピー(gitignored)
│ └── uploads/ ← 本番から コピー(gitignored)
└── .devcontainer/
├── devcontainer.json
└── docker-compose.yml ← devcontainer 用
docker-compose.yml の要点
一部抜粋、全文は下記付録に掲載しています
services:
wp:
build:
context: .
args:
USER_UID: '${UID:-1000}'
USER_GID: '${GID:-1000}'
INSTALL_CLAUDE: "false"
ports:
- "8080:80"
volumes:
- ./wordpress/wp-content:/var/www/html/wp-content
- ./docker/wp/wp-config.php:/var/www/html/wp-config.php:ro
- ./docker/wp/.htaccess:/var/www/html/.htaccess:ro
depends_on:
db:
condition: service_healthy
-
bind mount は
wp-contentのみ — コア(wp-admin/wp-includes/)は Docker WordPress image のものを利用 -
wp-config.phpと.htaccessは read-only bind — ローカル専用の設定(DB 接続先やWP_DEBUG等)を永続化 - UID は build arg でホスト UID に合わせる
Dockerfile の要点
UID 整合と Claude Code 同梱の部分だけ抜粋、全文は下記付録に掲載しています:
FROM wordpress:6.9-php8.4-apache
# WordPress 本体は image 由来
# www-data の UID をホストに揃える
ARG USER_UID=1000
ARG USER_GID=1000
RUN groupmod --non-unique -g ${USER_GID} www-data \
&& usermod --non-unique -u ${USER_UID} -d /home/www-data -s /bin/bash www-data \
&& mkdir -p /home/www-data/.claude /home/www-data/.config/gh \
&& chown -R www-data:www-data /var/www /home/www-data
# Claude Code を container 内にインストール(dev container 用)
ARG INSTALL_CLAUDE=false
RUN if [ "$INSTALL_CLAUDE" = "true" ]; then \
curl -fsSL https://claude.ai/install.sh -o /tmp/claude-install.sh && \
su www-data -c "bash /tmp/claude-install.sh" && \
rm /tmp/claude-install.sh; \
fi
公式 image の ENTRYPOINT(= docker-entrypoint.sh) と CMD(= apache2-foreground)はそのまま。手を加えるのは UID と Claude のインストールだけ、というシンプルな拡張です。
.gitignore の方針
自作テーマだけ追跡するホワイトリスト方式:
/wordpress/wp-content/themes/*
!/wordpress/wp-content/themes/index.php
!/wordpress/wp-content/themes/mytheme/
.gitignore の詳細は以下の派生記事にまとめました。
→ WordPress サイトの .gitignore レシピ — 自作テーマ・プラグインだけ git 管理するパターン
Intelephense の設定
{
"intelephense.environment.includePaths": [
"/var/www/html/wp-admin",
"/var/www/html/wp-includes"
]
}
詳細は以下の派生記事にまとめています。
→ ワークスペースに WordPress 本体が無くても VS Code の PHP 補完を効かせる — dev container × Intelephense
entrypoint の挙動
UID 整合の取り扱いを考えていた当初、docker-entrypoint.sh 内に wp-content の中身を tar --exclude で保護しつつコアを流し込む処理 があるのを見つけ、これを活用できないか試してみました。
結果はうまくいかず、関連の GitHub Issue / Discussion を調べたところ、これは Kubernetes 等の永続ストレージ運用や bind mount で永続化された /var/www/html とコアの配布を両立させる merge ロジック(末尾 参考[1][2])で、本構成には合わないと分かりました。
UID 整合は build 時の groupmod / usermod で完結させています。
まとめ
- WordPress を「自作テーマ・プラグインだけ git 管理、WordPress 本体は公式 image 任せ、wp-content は repo に置くが git 外」という構成にしたところ、快適な開発環境になりました。
- UID 整合と Claude Code 同梱で、AI エージェントを安全に同居させる開発環境ができる
- VS Code Intelephense にコンテナ内パスを
includePathsで渡せば、ホスト側に WordPress 本体を置かずに実装ジャンプできる - 開発環境を Docker 内 dev container として動かしているので、AI エージェントを使っても怖くない
- AI エージェントに WordPress 本体のコードも参照させたい場合は、コンテナ内の
/var/www/html配下を読みに行くよう指示してください
ニッチな構成ですが、似たような要件(WordPress で自作テーマ・プラグインを作成している、AI と協業したい)の人にとっては落としどころとして参考になるかもしれません。もし、より良い方法をご存じでしたら教えていただけますと幸いです。
関連記事
「なぜこの構成にしたか」「気づき」など設計判断の論考は別記事にまとめました。
→ WordPress の AI コーディングエージェント開発を安全に利用する Docker 環境構築 — 設計ノート
付録: 設定ファイル全文
本文中では要点だけを抜粋しましたので、ここに全文を掲載します。コピペで使うときの参考にどうぞ。
掲載する 4 ファイル:
-
docker-compose.yml— 基本構成(wp / db) -
Dockerfile— image のビルド定義 -
.devcontainer/devcontainer.json— VS Code Dev Container 用の設定 -
.devcontainer/docker-compose.yml— devcontainer 用 override(INSTALL_CLAUDE: "true"と.claude/gh設定の永続化)
docker-compose.yml
services:
wp:
build:
context: .
args:
INSTALL_CLAUDE: "false"
# host uid/gid に www-data を揃える。WSL の id が 1000:1000 と違う
# 環境では .env で UID / GID を設定してから docker compose build。
USER_UID: '${UID:-1000}'
USER_GID: '${GID:-1000}'
ports:
- "8080:80"
environment:
# 必要に応じて .env で上書きしてください
WORDPRESS_DB_HOST: '${WORDPRESS_DB_HOST:-db}'
WORDPRESS_DB_USER: '${WORDPRESS_DB_USER:-wpuser}'
WORDPRESS_DB_PASSWORD: '${WORDPRESS_DB_PASSWORD:-dummy}'
WORDPRESS_DB_NAME: '${WORDPRESS_DB_NAME:-wp}'
WORDPRESS_TABLE_PREFIX: '${WORDPRESS_TABLE_PREFIX:-wp_}'
WORDPRESS_DEBUG: '${WORDPRESS_DEBUG:-1}'
volumes:
- ./wordpress/wp-content:/var/www/html/wp-content
# ローカル専用 .htaccess (Read Only)
- ./docker/wp/.htaccess:/var/www/html/.htaccess:ro
# ローカル専用 wp-config.php (Read Only)
- ./docker/wp/wp-config.php:/var/www/html/wp-config.php:ro
depends_on:
db:
condition: service_healthy
db:
image: mysql:8.4
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
environment:
# wp 側と同じ ${VAR:-default} を共有して、.env で片方変えると両方追従する
MYSQL_ROOT_PASSWORD: '${MYSQL_ROOT_PASSWORD:-root}'
MYSQL_DATABASE: '${WORDPRESS_DB_NAME:-wp}'
MYSQL_USER: '${WORDPRESS_DB_USER:-wpuser}'
MYSQL_PASSWORD: '${WORDPRESS_DB_PASSWORD:-dummy}'
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p$$MYSQL_ROOT_PASSWORD --silent"]
interval: 5s
retries: 20
start_period: 30s
volumes:
db-data:
Dockerfile
FROM wordpress:6.9-php8.4-apache
# dev container として使うときに重宝する道具一式と、GitHub CLI(公式の apt repository を経由)。
# 好みで足し引きしてください。
RUN apt-get update && apt-get install -y --no-install-recommends \
nano less vim-tiny \
git ssh wget curl gnupg ca-certificates \
jq ripgrep procps rsync \
default-mysql-client \
&& install -dm 755 /etc/apt/keyrings \
&& curl -fsSL -o /etc/apt/keyrings/githubcli-archive-keyring.gpg https://cli.github.com/packages/githubcli-archive-keyring.gpg \
&& chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
> /etc/apt/sources.list.d/github-cli.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends gh \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# WP-CLI: バージョン固定
ARG WP_CLI_VERSION=2.12.0
ARG WP_CLI_SHA256=ce34ddd838f7351d6759068d09793f26755463b4a4610a5a5c0a97b68220d85c
RUN curl -fsSL -o /usr/local/bin/wp \
"https://github.com/wp-cli/wp-cli/releases/download/v${WP_CLI_VERSION}/wp-cli-${WP_CLI_VERSION}.phar" \
&& echo "${WP_CLI_SHA256} /usr/local/bin/wp" | sha256sum -c - \
&& chmod +x /usr/local/bin/wp
# www-data の UID をホストに揃える
ARG USER_UID=1000
ARG USER_GID=1000
RUN groupmod --non-unique -g ${USER_GID} www-data \
&& usermod --non-unique -u ${USER_UID} -d /home/www-data -s /bin/bash www-data \
&& mkdir -p /home/www-data/.claude /home/www-data/.config/gh \
&& chown -R www-data:www-data /var/www /home/www-data
ARG INSTALL_CLAUDE=false
RUN if [ "$INSTALL_CLAUDE" = "true" ]; then \
curl -fsSL https://claude.ai/install.sh -o /tmp/claude-install.sh && \
su www-data -c "bash /tmp/claude-install.sh" && \
rm /tmp/claude-install.sh; \
fi
ENV PATH="/home/www-data/.local/bin:$PATH"
.devcontainer/devcontainer.json
{
"name": "wordpress-local",
"dockerComposeFile": [
"../docker-compose.yml",
"docker-compose.yml"
],
"service": "wp",
"workspaceFolder": "/workspaces/wordpress-local",
"customizations": {
"vscode": {
"extensions": [
"Anthropic.claude-code",
"bmewburn.vscode-intelephense-client"
]
}
},
"remoteUser": "www-data"
}
.devcontainer/docker-compose.yml
dev container 用の追加設定。INSTALL_CLAUDE: "true" で Claude Code を image に同梱、リポをワークスペースディレクトリ /workspaces/wordpress-local に bind、.claude と gh の config を named volume に永続化しています。
services:
wp:
build:
args:
INSTALL_CLAUDE: "true"
volumes:
- .:/workspaces/wordpress-local
- claude-config:/home/www-data/.claude
- gh-config:/home/www-data/.config/gh
volumes:
claude-config:
gh-config:
参考
- docker-library/wordpress Discussion #623 — Custom theme / plugins running on Kubernetes — https://github.com/docker-library/wordpress/discussions/623 / 公式 image の wp-content merge ロジックが Kubernetes PVC・bind mount 永続化シナリオでどう機能するかの議論
-
docker-library/wordpress Issue #506 — "wp-content" persisted, "akismet" updated, container restarted, "akismet" downgraded — https://github.com/docker-library/wordpress/issues/506 / entrypoint が wp-content 配下を tar
--excludeするようになった経緯