PR
DEV

rsync + wp db export でWordPressを自動バックアップ【Mac × launchd】

rsync + wp db export でWordPressを自動バックアップ DEV

WordPressのバックアップ、プラグインに任せきりで本当に大丈夫でしょうか。UpdraftPlusやBackWPupは手軽ですが、サイトの規模が大きくなるとタイムアウトで止まったり、プラグイン自体のアップデートで設定が壊れたりすることがあります。

この記事では、rsync と wp db export を使い、プラグインに頼らずWordPressを丸ごと自動バックアップする方法を解説します。macOSの launchd で毎日自動実行し、さらにTime Machineと組み合わせることで「ローカルに二重の安全策」を作ります。

実際に筆者がVPS(KUSANAGI 9)と共用サーバーの両方で運用しているスクリプトをベースに、設計の考え方からリストア手順まで、そのまま使える内容をまとめました。

プラグインに頼らないバックアップ設計

WordPressのバックアップ方法は大きく分けて3つあります。

  1. レンタルサーバーの自動バックアップ機能を使う
  2. プラグイン(UpdraftPlus / BackWPupなど)を使う
  3. コマンドラインツール(rsync + wp-cli)で自分で組む

1と2は手軽な反面、いくつかの制約を抱えています。

方法メリットデメリット
サーバー自動バックアップ設定不要、サーバー側で自動実行保持期間が限られる(7〜14日が多い)。サーバー障害時に一緒に消える可能性がある
プラグイン管理画面から操作可能。クラウド連携も容易大規模サイトでタイムアウトしやすい。プラグイン更新で設定が壊れるリスクがある
rsync + wp-cliサーバー負荷が低い。差分転送で高速。保持ルールを自由に設計できるターミナル操作が必要。初期設定にやや手間がかかる

rsync + wp-cliによるバックアップの最大の利点は、バックアップデータがローカルマシンに保存される点です。サーバーが丸ごと飛んでもローカルにデータが残ります。プラグインのようにPHPの実行時間制限にも縛られません。

VPSを自分で管理している場合はもちろん、共用レンタルサーバーでもSSH接続とWP-CLIが使えれば同じ仕組みを構築できます。

全体設計と必要な環境

バックアップの全体像

今回構築するバックアップの全体フローは次のとおりです。

  1. サーバー上でDBエクスポート — wp db export でSQLダンプを生成し、gzip で圧縮
  2. rsyncでローカルに同期 — ファイルとDBダンプを差分転送でMacにダウンロード
  3. launchdで毎日自動実行 — Macが起動していれば毎日決まった時間に自動実行
  4. Time Machineが二重保護 — ローカルに保存されたバックアップをTime Machineがさらにバックアップ

ポイントは「サーバー側ではDBエクスポートだけ」「ファイル転送はすべてローカルから引っ張る」という設計です。サーバーにバックアップ用のcronを設定する必要がなく、管理がシンプルになります。

前提条件

  • Macのターミナル操作ができること
  • サーバーにSSH公開鍵認証でログインできること(パスワード認証でも動作しますが、自動化にはパスフレーズなしの鍵認証が必要です)
  • サーバーにWP-CLIがインストールされていること
  • ローカルにrsyncがインストールされていること(macOSは標準搭載)

WP-CLIの基本的な使い方は「WP-CLIの使い方 WordPress管理を劇的に効率化するコマンド」で解説しています。

SSH接続の設定がまだの方は、~/.ssh/config でホスト名のエイリアスを設定しておくと、スクリプトが読みやすくなります。

# ~/.ssh/config の設定例
Host my-vps
    HostName 203.0.113.10
    User kusanagi
    Port 22
    IdentityFile ~/.ssh/id_ed25519

バックアップスクリプトの全体像

ここからは実際に使えるバックアップスクリプトを解説します。VPS(KUSANAGI 9)向けと共用レンタルサーバー向けの2パターンを用意しました。どちらも設計思想は同じです。

VPS(KUSANAGI 9)向けスクリプト

KUSANAGI 9ではWordPressのプロファイルが /home/kusanagi/ 配下にまとめられています。このスクリプトはプロファイルを自動検出し、システム設定とWordPressデータを丸ごとバックアップします。

#!/bin/zsh

# --- 設定項目 ---
VPS_HOST="my-vps"                    # SSH configで定義したホスト名
BACKUP_ROOT="$HOME/Backups/VPS_Full" # ローカルの保存先

# macOS通知用の関数
notify() {
    local title=$1
    local message=$2
    osascript -e "display notification \"$message\" with title \"$title\""
}

echo "============================================"
echo "KUSANAGI 9 Auto-Detection Backup Started"
echo "============================================"

# -----------------------------------------------
# 1. システム設定ファイルの同期
# -----------------------------------------------
echo "[1/3] Syncing System Configurations..."
SYSTEM_DIRS=(
    "/etc/opt/kusanagi/"    # Nginx / PHP の設定
    "/etc/my.cnf.d/"        # MariaDB設定
    "/var/spool/cron/"      # Cron設定
    "/root/"                # rootユーザー設定
)

for S_DIR in "${SYSTEM_DIRS[@]}"; do
    mkdir -p "$BACKUP_ROOT$(dirname $S_DIR)"
    if ! rsync -avz --delete $VPS_HOST:"$S_DIR" "$BACKUP_ROOT$S_DIR" > /dev/null 2>&1; then
        notify "バックアップエラー" "システム設定 ($S_DIR) の同期に失敗しました"
    fi
done

# -----------------------------------------------
# 2. WordPressプロファイルの自動検出
# -----------------------------------------------
echo "[2/3] Detecting WordPress profiles..."
PROFILES=($(ssh $VPS_HOST "ls -F /home/kusanagi/ | grep '/$' | sed 's/\/$//'
    | grep -vE '^(backups|php|kusanagi)$'" 2>/dev/null))

if [ ${#PROFILES[@]} -eq 0 ]; then
    notify "バックアップエラー" "プロファイルが見つかりませんでした"
    exit 1
fi

echo "  Found profiles: ${PROFILES[@]}"

# -----------------------------------------------
# 3. 各プロファイルのDBエクスポート
# -----------------------------------------------
echo "[3/3] Exporting databases..."
for PROFILE in "${PROFILES[@]}"; do
    echo "  DB export for: $PROFILE"
    ssh $VPS_HOST "if [ -d /home/kusanagi/$PROFILE/DocumentRoot ]; then \
        nice -n 19 wp db export --allow-root \
            --path=/home/kusanagi/$PROFILE/DocumentRoot \
            /home/kusanagi/$PROFILE/db_backup.sql > /dev/null && \
        gzip -f /home/kusanagi/$PROFILE/db_backup.sql; \
    fi"
done

# -----------------------------------------------
# 4. 全データの一括同期
# -----------------------------------------------
echo "Syncing all profile data..."
mkdir -p "$BACKUP_ROOT/home/kusanagi"
rsync -avz --delete --force --delete-excluded \
    --exclude='backups' \
    --exclude='php' \
    --exclude='kusanagi' \
    --exclude='*/wp-content/cache/*' \
    --exclude='*/wp-content/uploads/kusanagi_html/*' \
    $VPS_HOST:/home/kusanagi/ "$BACKUP_ROOT/home/kusanagi/"

RC=$?

if [ $RC -eq 0 ] || [ $RC -eq 24 ]; then
    notify "バックアップ完了" "全データを正常に同期しました"
    echo "  [OK] Sync completed successfully"
else
    notify "バックアップエラー" "ファイル同期に失敗しました (エラーコード: $RC)"
    echo "  [ERROR] Sync failed (Exit code: $RC)"
fi

echo "============================================"
echo "Backup Process Finished"
echo "============================================"

スクリプトのポイントを解説します。

  • nice -n 19 でDBエクスポートの優先度を最低にし、本番サイトへの影響を最小限に抑えている
  • rsync --delete でサーバー側で削除されたファイルもローカルに反映する(完全ミラー)
  • --exclude でキャッシュファイルなど不要なデータを除外し、転送量を削減している
  • gzip -f でSQLダンプを圧縮して転送量を減らしている
  • 終了コード 24(一部のファイルが転送中に消えた)は正常終了として扱っている

共用レンタルサーバー向けスクリプト

共用サーバー(SSH接続とWP-CLIが使える環境)向けのスクリプトです。ドメインとサブドメインを自動検出してバックアップします。

#!/bin/zsh

# --- 設定項目 ---
SERVER_HOST="my-server"               # SSH configで定義したホスト名
BACKUP_ROOT="$HOME/Backups/Server_Full"
REMOTE_HOME="/home/username"          # リモートのホームディレクトリ

notify() {
    local title=$1
    local message=$2
    osascript -e "display notification \"$message\" with title \"$title\""
}

echo "============================================"
echo "Server Backup Started"
echo "============================================"

# ドメインディレクトリの自動検出
DOMAINS=($(ssh $SERVER_HOST \
    "ls -1 $REMOTE_HOME/ | grep -E '\.(org|net|com|jp)$'" 2>/dev/null))

if [ ${#DOMAINS[@]} -eq 0 ]; then
    notify "バックアップエラー" "ドメインが見つかりませんでした"
    exit 1
fi

echo "Found domains: ${DOMAINS[@]}"

WP_COUNT=0

for DOMAIN in "${DOMAINS[@]}"; do
    echo "Processing: $DOMAIN"
    DOMAIN_PATH="$REMOTE_HOME/$DOMAIN"
    PUBLIC_HTML="$DOMAIN_PATH/public_html"

    # WordPress検出 → DBエクスポート
    process_wp_site() {
        local wp_path=$1
        local backup_dir=$2
        local site_name=$3

        echo "  [WP] DB export for $site_name..."
        ssh $SERVER_HOST "if [ -f '$wp_path/wp-config.php' ]; then \
            mkdir -p '$backup_dir' && \
            nice -n 19 wp db export \
                --path='$wp_path' \
                '$backup_dir/db_backup.sql' 2>/dev/null && \
            gzip -f '$backup_dir/db_backup.sql' 2>/dev/null; \
        fi"
        ((WP_COUNT++))
    }

    # メインドメインのチェック
    ssh $SERVER_HOST "[ -f '$PUBLIC_HTML/wp-config.php' ]" 2>/dev/null
    if [ $? -eq 0 ]; then
        process_wp_site "$PUBLIC_HTML" "$DOMAIN_PATH/backup" "$DOMAIN"
    fi

    # サブドメインの自動検出
    SUBDOMAINS=($(ssh $SERVER_HOST \
        "ls -1 '$PUBLIC_HTML' 2>/dev/null \
        | grep -E '\.[a-z]+$'" 2>/dev/null))

    for SUB in "${SUBDOMAINS[@]}"; do
        SUB_PATH="$PUBLIC_HTML/$SUB"
        ssh $SERVER_HOST "[ -f '$SUB_PATH/wp-config.php' ]" 2>/dev/null
        if [ $? -eq 0 ]; then
            process_wp_site "$SUB_PATH" \
                "$DOMAIN_PATH/backup/$SUB" "$SUB"
        fi
    done
done

# 全データ一括同期
echo "Syncing all data..."
mkdir -p "$BACKUP_ROOT$REMOTE_HOME"
rsync -avz --delete --force --delete-excluded \
    --exclude='*/wp-content/cache/*' \
    $SERVER_HOST:$REMOTE_HOME/ "$BACKUP_ROOT$REMOTE_HOME/" > /dev/null 2>&1

RC=$?

if [ $RC -eq 0 ] || [ $RC -eq 24 ]; then
    notify "バックアップ完了" \
        "${#DOMAINS[@]}ドメイン (WP: $WP_COUNT) を同期しました"
else
    notify "バックアップエラー" \
        "同期に失敗しました (エラーコード: $RC)"
fi

echo "============================================"
echo "Backup Process Finished"
echo "============================================"

rsync除外ルールの設計

rsyncの --exclude オプションで除外すべきファイルやディレクトリは、環境に応じて適切に設定する必要があります。以下は代表的な除外対象です。

除外対象理由
*/wp-content/cache/*キャッシュプラグインの生成ファイル。再生成されるため不要
*/wp-content/uploads/kusanagi_html/*KUSANAGIのページキャッシュ。再生成されるため不要
*/wp-content/debug.logデバッグログ。バックアップ対象としては不要
*.tmp / *.swp一時ファイル。バックアップの必要なし

一方、絶対に除外してはいけないものもあります。

  • wp-content/uploads/ — メディアファイル一式。これを失うと記事の画像がすべて消えます
  • wp-content/themes/ — カスタムテーマのソースコード
  • wp-content/plugins/ — プラグイン本体とその設定
  • wp-config.php — DB接続情報や認証キーなど最重要ファイル
  • .htaccess — パーマリンクやリダイレクトの設定

rsyncの除外ルールについてより詳しく知りたい場合は「rsyncコマンドとスラッシュの有無の違い」も参考にしてください。

DBダンプの保存先に関する注意

DBのエクスポート先を public_html(ドキュメントルート)の中に保存してしまうと、第三者がブラウザからSQLファイルをダウンロードできてしまいます。これはデータベースの中身(ユーザー情報やパスワードハッシュを含む)が丸見えになる深刻なセキュリティリスクです。

上記のスクリプトでは、ドキュメントルートの外(例: /home/kusanagi/profile_name/db_backup.sql.gz や /home/username/example.com/backup/db_backup.sql.gz)に保存する設計にしています。必ずWeb公開ディレクトリの外に保存してください。

macOS launchd による定期実行

スクリプトを手動で実行するだけでも十分ですが、macOSの launchd を使えば毎日自動で実行できます。Linuxの cron に相当するmacOS標準の仕組みです。

plistファイルの作成

以下の内容で ~/Library/LaunchAgents/ にplistファイルを作成します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.vpsbackup</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/zsh</string>
        <string>/Users/username/Backups/scripts/vps_backup.sh</string>
    </array>
    <key>StartCalendarInterval</key>
    <array>
        <dict>
            <key>Hour</key>
            <integer>4</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>16</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/vps_backup.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/vps_backup_error.log</string>
</dict>
</plist>

ファイル名は com.user.vpsbackup.plist のように、Label と一致させてください。

launchdへの登録と動作確認

# plistの文法チェック
plutil -lint ~/Library/LaunchAgents/com.user.vpsbackup.plist

# 既存登録があれば解除してから再登録
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.user.vpsbackup.plist 2>/dev/null
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.user.vpsbackup.plist

# 手動で即時実行してテスト
launchctl kickstart -k gui/$(id -u)/com.user.vpsbackup

# 実行ログの確認
tail -f /tmp/vps_backup.log /tmp/vps_backup_error.log

登録を解除する場合は launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.user.vpsbackup.plist を実行します。

launchd設定のポイント

  • StartCalendarInterval を配列で定義すると1日に複数回実行できます。上の例では毎日 04:00 と 16:00 に実行します
  • Macがスリープ中や電源オフの場合は実行されません。次に起動したときに実行されます
  • RunAtLoad を true にすると、ログイン直後にも1回実行されます
  • StandardOutPath と StandardErrorPath を分けると、通常ログとエラーログを切り分けて追跡しやすくなります
  • ログは /tmp/ に出力しているため、再起動すると消えます。永続化したい場合は ~/Library/Logs/ などに変更してください

Time Machine との二重保護

rsyncでローカルに同期したバックアップデータは、macOSのTime Machineが自動的にバックアップ対象に含めます。つまり、特別な設定をしなくても次のような「二重の保護」になります。

  1. 1層目: rsync — サーバー → Mac(毎日の最新スナップショット)
  2. 2層目: Time Machine — Mac → 外付けディスク(世代管理付き)

この構成のメリットは「いつの状態にも戻せる」ことです。rsync単体ではサーバーの最新状態しか保持できませんが、Time Machineが過去の状態を時系列で保持してくれます。

たとえば「3日前のデータベースの状態に戻したい」という場合、Time Machineから db_backup.sql.gz の3日前のバージョンを取り出せます。

リストア手順

バックアップは「取って終わり」ではなく、リストアの手順まで確認しておくことが重要です。ここでは主なリストアパターンを説明します。

ファイルのリストア

ローカルに保存されたバックアップから、rsyncでサーバーにファイルを戻します。

# まずドライランで確認(実際には転送しない)
rsync -avzn --delete \
    ~/Backups/VPS_Full/home/kusanagi/profile_name/ \
    my-vps:/home/kusanagi/profile_name/

# 問題なければ実行
rsync -avz --delete \
    ~/Backups/VPS_Full/home/kusanagi/profile_name/ \
    my-vps:/home/kusanagi/profile_name/

-n(ドライラン)オプションを必ず先に実行してください。意図しないファイルの上書きや削除を防げます。

データベースのリストア

ローカルに保存されたSQLダンプをサーバーに転送してインポートします。

# 1. ダンプファイルを解凍
gunzip ~/Backups/VPS_Full/home/kusanagi/profile_name/db_backup.sql.gz

# 2. サーバーに転送
scp ~/Backups/VPS_Full/home/kusanagi/profile_name/db_backup.sql \
    my-vps:/home/kusanagi/profile_name/

# 3. サーバー上でインポート
ssh my-vps "wp db import \
    --path=/home/kusanagi/profile_name/DocumentRoot \
    /home/kusanagi/profile_name/db_backup.sql"

#  4. URLの置換が必要な場合(ドメイン変更時など)
ssh my-vps "wp search-replace \
    'https://old-domain.com' 'https://new-domain.com' \
    --path=/home/kusanagi/profile_name/DocumentRoot \
    --all-tables"

wp db import は既存のテーブルを上書きするため、実行前に現在のDBもエクスポートしておくことを推奨します。

特定の日付のバックアップからリストアする場合

Time Machineと組み合わせている場合は、Finderから該当日時のバックアップディレクトリに入り、そこからファイルをコピーして使います。

# Time Machineのバックアップ先を確認
tmutil latestbackup

# 過去の日付からダンプファイルを見つける例
# Finderの「Time Machineに入る」から該当ファイルを選択するのが簡単です

トラブルシューティング

rsyncがPermission deniedで失敗する

SSH鍵認証の設定を確認してください。launchd経由で実行する場合、シェルの環境と異なる権限で動作することがあります。

# SSH接続テスト
ssh -v my-vps "echo OK"

# 鍵のパーミッション確認
ls -la ~/.ssh/id_ed25519
# → -rw------- であることを確認(600)

launchdでスクリプトが実行されない

よくある原因と対処法です。

  • plistの文法エラー — plutil -lint ファイル名.plist でチェックしてください
  • PATHが通っていない — 必要に応じてplistの EnvironmentVariables に PATH を追記してください
  • HOMEが設定されていない — ~ がlaunchd環境では展開されないため、HOME を環境変数で指定してください
  • Macがスリープ中だった — launchdのスケジュール実行は、Macが起動している時間帯に設定してください
# launchdの登録状態を確認
launchctl list | grep vpsbackup

# ログを確認
cat /tmp/vps_backup.log
cat /tmp/vps_backup_error.log

wp db export が動作しない

  • WP-CLIがインストールされていない — ssh my-vps "wp --version" で確認してください
  • パスの指定が間違っている — --path オプションでWordPressのインストールディレクトリを正確に指定してください
  • 権限の問題 — VPSで --allow-root が必要な場合があります

rsyncの転送が遅い

  • -z オプション(圧縮転送)がついているか確認してください
  • --exclude でキャッシュや不要ファイルを除外しているか確認してください
  • 初回は全転送になるため時間がかかりますが、2回目以降は差分転送になるため大幅に高速化されます

よくある質問(FAQ)

バックアップの実行頻度はどのくらいが適切ですか?

記事の更新頻度によって判断してください。毎日更新しているサイトなら1日1回、週1〜2回の更新なら週に2〜3回で十分です。今回のスクリプトはrsyncの差分転送を利用しているため、毎日実行してもサーバー負荷や転送量は最小限です。

プラグインのバックアップとスクリプトのバックアップ、どちらがよいですか?

技術的に可能であればスクリプトをおすすめします。プラグインはPHPの実行時間制限に縛られるため、画像が多いサイトではタイムアウトしやすくなります。一方、ターミナル操作に慣れていない方はUpdraftPlusなどのプラグインのほうが確実です。両方を併用するのも有効な選択肢です。

Windows環境でも同じ方法は使えますか?

WSL2(Windows Subsystem for Linux)を使えば同等の環境を構築できます。rsyncやsshコマンドはWSL2内のLinux環境で動作します。ただし、launchdの代わりにWindowsのタスクスケジューラを使う必要があります。

複数のサーバーをバックアップしたい場合は?

サーバーごとにスクリプトとplistファイルを用意すれば、それぞれ独立して自動実行できます。実行時間をずらしておくと、ネットワーク帯域の競合を避けられます。

まとめ

この記事では、rsync + wp db exportによるWordPressの自動バックアップ環境の構築方法を解説しました。要点を振り返ります。

  • rsync + wp db export でファイルとデータベースをまとめてローカルにバックアップ
  • DBダンプはドキュメントルート外に保存する(セキュリティ上の必須事項)
  • macOSのlaunchdで毎日自動実行。plistファイルでスケジュールを管理
  • Time Machineとの組み合わせで、世代管理付きの二重保護を実現
  • リストアはrsync + wp db importで行う。ドライランで確認してから実行

プラグインに頼らないバックアップ体制を作っておくと、サーバー障害やプラグインの不具合に巻き込まれたときも慌てずに済みます。一度スクリプトを設定すれば、あとは放置で毎日バックアップが取れるので、最初の設定だけ頑張ってみてください。

rsyncを使ったローカルから本番へのデプロイ方法については「wp db + rsync でローカルから本番に安全デプロイ」で詳しく解説しています。バックアップと合わせてデプロイ環境も整えておくと、WordPress運用がさらに安全かつ効率的になります。

この記事を書いた人
Naokazu Yokoo

エンジニア歴20年以上。現在はWeb / AI領域で活動中。
開発環境・サーバー環境ともに速度と効率性を追求しています。

Follow Naokazu Yokoo
Follow Naokazu Yokoo

Comment

タイトルとURLをコピーしました