2026年7月14日
2026年7月14日
WP-CLIとrsyncでWordPressのデプロイを自動化するスクリプトの作り方
WordPressのテーマやプラグインを本番へ送る作業は、「WP-CLIでバックアップとメンテナンスモード」「rsyncで差分転送」「WP-CLIでキャッシュ削除と確認」という流れを1本のシェルスクリプトにまとめると、毎回同じ手順で安全に繰り返せるようになります。この記事では、WP-CLIのエイリアス設定、rsyncの除外と削除の考え方、途中で失敗しても後始末をするスクリプトの書き方、ロールバックの用意までを、実際に使える形で説明します。
コマンドのオプションはバージョンで変わることがあります。以下は執筆時点(2026年10月現在)のWP-CLI 2系とrsync 3系を前提にした例です。スクリプトは自分の環境に合わせて書き換え、ステージング環境で試してから本番に使ってください。
この方法が向いているケース
rsyncとWP-CLIによるデプロイは、SSHで接続できるVPSやクラウドサーバー、SSHとWP-CLIが使えるレンタルサーバーに向いています。GitHub ActionsなどのCIを使わなくても、手元のパソコンから1コマンドで実行できるのが利点です。
一方、送るのは「コード」(自作のテーマ・プラグイン・mu-plugins)に限ります。データベースと wp-content/uploads は本番で日々変わるデータなので、手元から上書きしてはいけません。この線引きがデプロイスクリプト全体の前提になります。
準備1:SSHとWP-CLIのエイリアス
まず、パスワードなしでSSH接続できるよう鍵認証を設定します。本番サーバーにもWP-CLIをインストールしておきます。
次に、手元のプロジェクトのルートに wp-cli.yml を置き、本番を指すエイリアスを定義します。こうすると、手元から wp @prod ... と打つだけで、SSH経由で本番のWP-CLIを実行できます。
# wp-cli.yml(プロジェクトのルート)
@prod:
ssh: deploy@prod.example.com/var/www/example.com
@staging:
ssh: deploy@staging.example.com/var/www/example.com
ssh: の値は「ユーザー名@ホスト名」に続けてWordPressのディレクトリのパスを書きます。ポートを変えている場合は deploy@prod.example.com:2222/var/www/example.com の形にします。設定できたら、次のコマンドで接続を確かめます。
wp @prod core version
wp @prod option get siteurl
siteurl が本番のURLを返せば、エイリアスは正しく動いています。ステージングと本番を取り違えないよう、スクリプトの最初でもこの確認を行います。
準備2:rsyncの除外リスト
送らないファイルを .deployignore という名前でまとめておきます。rsyncの --exclude-from で読み込みます。
# .deployignore
.git/
.github/
node_modules/
.env
*.log
.DS_Store
src/scss/
ここで大事なのが --delete との関係です。--delete は「送り元に無いファイルを送り先から消す」オプションで、古いファイルが残らない反面、パスを間違えると本番のファイルを消してしまいます。ただし、除外リストに書いたものは、--delete-excluded を付けない限り送り先でも削除されません。本番側にだけ置くファイル(ログやキャッシュ)は除外リストに書いておくと、誤って消されるのを防げます。
また、rsyncは送り元の末尾にスラッシュがあるかどうかで動きが変わります。themes/my-theme/ は「中身を送る」、themes/my-theme は「ディレクトリごと送る」です。スクリプトでは、送り元と送り先の両方に末尾スラッシュを付ける書き方に統一すると迷いません。
デプロイスクリプト
以上をまとめたスクリプトの例です。既定では --dry-run(転送予定の表示のみ)で動き、DRY_RUN=0 を付けたときだけ本番に反映します。
#!/usr/bin/env bash
# deploy.sh — テーマ・プラグインを本番へ送る
set -euo pipefail
ALIAS="@prod"
REMOTE="deploy@prod.example.com"
REMOTE_ROOT="/var/www/example.com"
SITE_URL="https://www.example.com/"
TARGETS=(
"wp-content/themes/my-child-theme"
"wp-content/plugins/my-plugin"
)
DRY_RUN="${DRY_RUN:-1}"
STAMP="$(date +%Y%m%d-%H%M%S)"
BACKUP_DIR="/var/www/deploy-backups/${STAMP}"
cd "$(dirname "$0")"
# 1. 事前確認:未コミットの変更があれば止める
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "未コミットの変更があります。コミットしてから実行してください。" >&2
exit 1
fi
echo "送る版: $(git rev-parse --short HEAD)"
echo "送り先: $(wp "$ALIAS" option get siteurl)"
RSYNC_OPTS=(-az --delete --itemize-changes --exclude-from=.deployignore)
if [[ "$DRY_RUN" == "1" ]]; then
for t in "${TARGETS[@]}"; do
rsync "${RSYNC_OPTS[@]}" --dry-run "./${t}/" "${REMOTE}:${REMOTE_ROOT}/${t}/"
done
echo "dry-run のみ実行しました。反映するには DRY_RUN=0 ./deploy.sh"
exit 0
fi
# 2. データベースのバックアップ(手元に保存)
mkdir -p backups
wp "$ALIAS" db export - | gzip > "backups/prod-${STAMP}.sql.gz"
# 3. メンテナンスモード。失敗しても抜けるように trap で解除する
cleanup() {
wp "$ALIAS" maintenance-mode deactivate || true
}
trap cleanup EXIT
wp "$ALIAS" maintenance-mode activate
# 4. 転送。上書き・削除されるファイルは BACKUP_DIR に退避
for t in "${TARGETS[@]}"; do
rsync "${RSYNC_OPTS[@]}" --backup --backup-dir="${BACKUP_DIR}/${t}" \
"./${t}/" "${REMOTE}:${REMOTE_ROOT}/${t}/"
done
# 5. 後処理
wp "$ALIAS" cache flush
# 6. メンテナンスモード解除(trap でも解除されるが、ここで明示的に)
wp "$ALIAS" maintenance-mode deactivate
trap - EXIT
# 7. 表示の確認
code="$(curl -s -o /dev/null -w '%{http_code}' "$SITE_URL")"
echo "トップページのHTTPステータス: ${code}"
if [[ "$code" != "200" ]]; then
echo "200以外が返りました。表示を確認してください。退避先: ${BACKUP_DIR}" >&2
exit 1
fi
echo "デプロイ完了: ${STAMP}"
各手順の意味とつまずきやすい点
**set -euo pipefail** は、途中のコマンドが失敗したらその場で止めるための設定です。これが無いと、バックアップに失敗しても転送が進んでしまいます。pipefail は、wp db export - | gzip のようなパイプの前半が失敗したことも検出するために入れています。
未コミットの確認 は、「手元で途中まで直したファイル」がうっかり本番に出るのを防ぎます。送った版のコミットIDを表示しておくと、あとで「本番に何が入っているか」を追えます。
**wp db export -** は、ダンプを標準出力に出すオプションです。エイリアス経由で実行すると、本番のダンプがSSH越しに手元へ流れてくるので、本番のディスクに一時ファイルを残さずに済みます。データベースが大きい場合は時間がかかるため、サーバー側のバックアップの仕組みと併用してもかまいません。ダンプには個人情報が含まれることがあるので、backups/ はGitの管理対象から外し、不要になったら削除します。
メンテナンスモード は、ファイルが入れ替わる途中の中途半端な状態を訪問者に見せないためのものです。wp maintenance-mode activate は WordPress ルートに .maintenance ファイルを作ります。スクリプトが途中で止まってもこのファイルが残らないよう、trap ... EXIT で解除処理を登録しています。メンテナンスモードが残ると、サイト全体に「メンテナンス中」の表示が出続けるので、この後始末は省かないでください。
**--backup --backup-dir** は、上書き・削除されるファイルを指定したディレクトリへ退避する rsync のオプションです。送り先から見た相対パスにすると意図しない場所に作られるため、例のようにフルパスで指定します。表示に問題が出たら、退避したファイルを元の場所に戻すことで、直前の状態に戻せます。
**wp cache flush** はオブジェクトキャッシュを消去します。ページキャッシュ系のプラグインを使っている場合は、そのプラグインが提供するWP-CLIコマンドや管理画面から別途キャッシュを削除します(コマンドの有無と名前はプラグインごとに違います)。
本番に反映する前の確認
スクリプトを作ったら、いきなり本番に流さず、次の順番で試すと安全です。
./deploy.sh(dry-run)で、転送・削除される予定のファイル一覧を確認する。*deletingと表示された行は、送り先で削除されるファイルです。- エイリアスを
@stagingに変えた版でステージングに反映し、表示と機能を確認する。 - 問題がなければ
DRY_RUN=0 ./deploy.shで本番に反映する。
dry-runの出力で、想定していないディレクトリが大量に削除予定になっていたら、送り先のパスか末尾スラッシュの指定が間違っています。そのまま本番に流さず、スクリプトを見直してください。
運用で気をつけること
- プラグインの更新とデータベースの変更: 自作プラグインの新しい版がデータベースのテーブル構造を変える場合、ファイルを送るだけでは不十分なことがあります。プラグイン側の更新処理が動くか、ステージングで確認しておきます。
- 配布プラグインの扱い: WordPress.org のプラグインや有料プラグインは、デプロイ対象に入れず本番の管理画面や
wp @prod plugin updateで更新する、と決めておくと二重管理を避けられます。 - 本番での直接編集を止める: 本番の
wp-config.phpにdefine( 'DISALLOW_FILE_EDIT', true );を入れ、管理画面からのテーマ・プラグインファイルの編集を無効にします。本番で直接書き換えると、次のデプロイで--deleteにより上書きされて消えます。 - 入れ替えの一瞬も止めたくない場合: リリースごとにディレクトリを分け、シンボリックリンクを切り替える方式(いわゆるアトミックデプロイ)が向いています。rsyncでの上書き方式より仕組みは複雑になります。
まとめ
- 送るのは自作テーマ・プラグインだけ。データベースと uploads は本番を正とする。
wp-cli.ymlのエイリアスで、手元からwp @prodを使えるようにする。- rsyncは除外リストと末尾スラッシュに注意し、
--deleteを使うときは dry-run で削除予定を確認する。 - スクリプトは
set -euo pipefailで途中停止させ、trapでメンテナンスモードを解除する。 --backup --backup-dirで退避先を作り、問題があればすぐ戻せるようにしておく。