第 6 回で、レンタルサーバーのデータベースを毎晩 NAS にバックアップするようにしました。ところが WordPress のサイトは、データベースだけでは元に戻りません。記事の画像・テーマ・プラグインは、wp-content というフォルダのファイルとして、サーバーに置かれているからです。
この回では、第 6 回と同じ形で、wp-content を毎週 1 回 NAS へ完全自動でバックアップします。あわせて、お問い合わせの添付など、個人情報が入るフォルダはバックアップから外す設定も書きます。
この回の前にできていること
- 第 1〜3 回:ラズパイを始める・OS を書き込む・PC から SSH でつなぐ
- 第 5 回:ラズパイに自宅の NAS をつなぐ(NAS のフォルダが /mnt/nas に見えている)
- 第 6 回:データベースのバックアップを完全自動化(サーバーに SSH でつなげる・~/backup がある)
この回でできるようになること
- 毎週日曜の 3:10 に、サイトの wp-content が 1 つの tar.gz にまとまって NAS に保存される
- 新しい 4 本(4 週分)を残し、古いものは自動で消える
- 個人情報が入るフォルダや、作り直せるキャッシュは、バックアップに含めない
先に:この仕組みが要る人・要らない人
多くのレンタルサーバーには、自動のバックアップがあります。エックスサーバーなら、何もしなくてもファイルは 7 日分、データベースは 14 日分が保存されていて、サーバーパネルから戻せます(日数はサーバーパネルの「バックアップ」で見られます)。
この仕組みが向いている人
- テーマやプラグインを git で管理していない(手動で直したファイルが、サーバーにしかない)
- 7 日より前の状態に戻したいことがある
- サーバーの会社の外(自宅)にも、バックアップを持っておきたい
サーバーの自動のバックアップだけでよい人
- テーマやプラグインは git で管理していて、いつでも作り直せる
- 戻したくなるのは、たいてい数日のうち
- 個人情報が入るファイルを、自宅にはなるべく持ちたくない
わたしの家は「サーバーの自動のバックアップだけでよい人」のほうです。テーマとプラグインは git で管理していて、アップロードされたファイルには個人情報が入ることもあるので、ファイルはエックスサーバーの 7 日分に任せることにしました。この記事のために、家のラズパイで数サイトを実際に動かして数字を取ったあと、毎週の予定は外しています。
「この仕組みが向いている人」に当てはまる人は、このあとの手順で作ってください。
仕組みの流れ
第 6 回とほとんど同じです。違うのは、サーバーが出すものが「データベース」ではなく「wp-content の tar.gz」になることと、毎晩ではなく毎週にすることです。

- ① ラズパイが、毎週日曜の 3:10 にサーバーへつなぐ(ファイル専用の鍵で)
- ② サーバーの側で、wp-content を tar.gz にまとめて出す(外すフォルダは除く)
- ③ ラズパイが受け取って、そのまま NAS に書く(ラズパイの SD カードには書かない)
- ④ 壊れていないかを確認してから、正式な名前を付け、新しい 4 本より古いものを消す
含めるもの・外すもの
含めるもの(wp-content の下)
- uploads(記事の画像など)
- themes(テーマ)
- plugins(プラグイン)
外すもの(--exclude で指定)
- 個人情報が入るフォルダ(例:Contact Form 7 の添付の一時置き場 uploads/wpcf7_uploads)
- キャッシュ(cache)・更新の一時置き場(upgrade):作り直せる
- ログ(*.log)
個人情報が入るフォルダは、使っているプラグインで変わります。会員の添付・注文の書類・応募のファイルなどを受け付けているなら、そのフォルダの名前を調べて、外すものに足します。分からないときは、サーバーで ls ~/example.com/public_html/wp-content/uploads を実行して、年月(2026 など)でないフォルダを見てみてください。
手順 A:サーバーに wp-content を出すスクリプトを設置する
第 6 回の手順 A と同じように、PC から SSH でサーバーにつないで、次のスクリプトを ~/bin/backup-files.sh に設置します。ラズパイから「files 名前」と呼ばれたときだけ、決めたサイトの wp-content を出します。
ここだけ自分の環境に変える(手順 A)
- dir_of の中の「mysite」と「$HOME/example.com/public_html/wp-content」:自分で決める名前と、そのサイトの wp-content の場所(エックスサーバーなら「ホーム/ドメイン/public_html/wp-content」)
- EXCLUDES:外すフォルダ。個人情報が入るフォルダがあれば、同じ形で 1 行ずつ足す
#!/bin/bash
# ~/bin/backup-files.sh(レンタルサーバーに置く)
# ラズパイから「files 名前」と呼ばれたときだけ、wp-content を tar.gz にして出す
set -euo pipefail
umask 077
# ===== ここだけ自分の環境に変える =====
# 名前と、その名前で取る wp-content のフォルダ(何行でも足せる)
dir_of() {
case "$1" in
mysite) echo "$HOME/example.com/public_html/wp-content" ;;
# blog) echo "$HOME/blog.example.com/public_html/wp-content" ;;
*) return 1 ;;
esac
}
# 含めないもの:個人情報が入るもの・作り直せるもの(wp-content からの場所で書く)
EXCLUDES=(
--exclude='wp-content/uploads/wpcf7_uploads' # Contact Form 7 の添付(一時置き場)
--exclude='wp-content/cache' # キャッシュ(作り直せる)
--exclude='wp-content/upgrade' # 更新の一時置き場
--exclude='*.log'
)
# =====================================
read -r cmd name extra <<< "${SSH_ORIGINAL_COMMAND:-}"
if [ "${cmd:-}" != "files" ] || [ -z "${name:-}" ] || [ -n "${extra:-}" ]; then
echo "使えるのは files <名前> だけ" >&2; exit 2
fi
src=$(dir_of "$name") || { echo "知らない名前:$name" >&2; exit 2; }
[ -d "$src" ] || { echo "$src がない" >&2; exit 3; }
cd "$(dirname "$src")"
# 取っているあいだにファイルが変わると、tar は 1 を返す(バックアップは最後まで作られる)。ラズパイの側で 1 は受け入れる
nice -n 15 tar czf - "${EXCLUDES[@]}" wp-content
nano ~/bin/backup-files.sh # 上のスクリプトを貼り付けて保存
chmod 700 ~/bin/backup-files.sh
サーバーには、バックアップのファイルを作りません。tar がまとめたデータを、そのままラズパイに流します。nice -n 15 で、サイトの表示より後回しにして動かすので、サイトが重くなりにくくなります。
手順 B:ラズパイに鍵とスクリプトを設置する
1. ファイル専用の鍵を作る
第 6 回の鍵(データベース用)とは別に、ファイルのためだけの鍵を作ります。1 つの鍵には 1 つの処理だけをさせておくと、どちらかの鍵がもし漏れても、できることが限られます。
ssh-keygen -t ed25519 -f ~/.ssh/backup_files_ed25519 -N "" -C "raspi-backup-files"
cat ~/.ssh/backup_files_ed25519.pub
2. サーバーに鍵を登録する
サーバーの ~/.ssh/authorized_keys の最後に、次の 1 行を足します。command= で、この鍵でつないだときは手順 A のスクリプトしか動きません。from= の付け方・外し方は、第 6 回と同じです。
command="/home/サーバーのユーザー名/bin/backup-files.sh",no-pty,no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA…(ラズパイで表示した公開鍵)… raspi-backup-files
3. ラズパイからサーバーへのつなぎ方を書く
ラズパイの ~/.ssh/config に、次の 5 行を足します。第 6 回の backup-server と、鍵(IdentityFile)と名前だけが違います。
Host backup-files
HostName example.xsrv.jp
User サーバーのユーザー名
Port 10022
IdentityFile ~/.ssh/backup_files_ed25519
4. ラズパイにバックアップのスクリプトを設置する
ラズパイの ~/backup/files_backup.sh に、次のスクリプトを置きます。
ここだけ自分の環境に変える(手順 B)
- SERVER:3. で書いた Host の名前(そのままなら backup-files)
- NAS_DIR:第 6 回と同じ、NAS のバックアップを置くフォルダ
- KEEP:残す数。毎週 1 本なので、4 なら 4 週分
#!/bin/bash
# ~/backup/files_backup.sh(ラズパイに置く)
# 使い方:~/backup/files_backup.sh <名前> [now|weekly]
# now … 今すぐ 1 本取る(名前は files_now_…。古いものを消す処理と「新しい KEEP 本」の数の外)
# weekly … 毎週の分。取ったあと、新しい KEEP 本より古いものを消す
set -uo pipefail
# ===== ここだけ自分の環境に変える =====
SERVER="backup-files" # ~/.ssh/config の Host の名前
NAS_DIR="/mnt/nas/backup" # 第 5 回でつないだ NAS の、バックアップを置くフォルダ
KEEP=4 # 残す数(毎週 1 本なら 4 週分)
# =====================================
name="${1:-}"; kind="${2:-now}"
[ -n "$name" ] || { echo "使い方:$0 <名前> [now|weekly]"; exit 2; }
LOG="$HOME/backup/backup.log"
log() { echo "$(date '+%F %T') $name files $*" >> "$LOG"; }
# 失敗の記録は、第 6 回のデータベースの FAILED とは別の名前にする(片方の成功で、もう片方の失敗が消えないように)
FAILED="$HOME/backup/FAILED_FILES"
fail() { log "失敗:$1"; touch "$FAILED"; exit 1; }
[ -d "$NAS_DIR" ] && [ -w "$NAS_DIR" ] || fail "NAS のフォルダに書けない($NAS_DIR)"
dir="$NAS_DIR/$name"
mkdir -p "$dir" || fail "フォルダを作れない($dir)"
if [ "$kind" = weekly ]; then prefix="files_"; else prefix="files_now_"; fi
file="$dir/${prefix}$(date +%Y%m%d_%H%M).tar.gz"
start=$(date +%s)
# 1. サーバーで作った tar.gz を、NAS に .part の名前で書く
ssh -o BatchMode=yes -o ServerAliveInterval=30 "$SERVER" "files $name" > "$file.part" 2>>"$LOG"
rc=$?
# tar の 1(取っているあいだにファイルが変わった)は受け入れて、下の確認で決める
if [ $rc -ne 0 ] && [ $rc -ne 1 ]; then rm -f "$file.part"; fail "送る途中で止まった(rc=$rc)"; fi
# 2. 壊れていないかを確認する(gzip として読める・中にファイルが並んでいる)
count=$(tar tzf "$file.part" 2>/dev/null | wc -l)
if ! gzip -t "$file.part" 2>/dev/null || [ "$count" -lt 10 ]; then
mv "$file.part" "$file.broken"; fail "確認で NG(ファイル数 $count・$file.broken)"
fi
# 3. 正式な名前にする
mv "$file.part" "$file"
log "OK $file size=$(du -h "$file" | cut -f1) files=$count $(( $(date +%s) - start ))s"
rm -f "$FAILED"
# 4. 毎週の分のときだけ、新しい KEEP 本より古いものを消す
if [ "$kind" = weekly ]; then
ls -1 "$dir"/files_[0-9]*.tar.gz 2>/dev/null | sort | head -n -"$KEEP" | while read -r f; do
rm -f "$f" && log "消した(残す数の外):$f"
done
fi
手順 C:手動で 1 回動かしてみる
chmod 700 ~/backup/files_backup.sh
ssh backup-files # 初回だけ。yes を聞かれたら yes と打つ。「使えるのは files <名前> だけ」と出て切れれば OK
~/backup/files_backup.sh mysite now # 手動で 1 回取る
tail -n 1 ~/backup/backup.log # 最後の行が OK なら成功
うまくいくと、ログの最後の行がこうなります。下の表の参考サイト B で取ったときの数字です。
2026-10-28 11:00:07 mysite files OK /mnt/nas/backup/mysite/files_20261028_1055.tar.gz size=526M files=17752 280s
files= は、tar.gz の中に並んでいるファイルとフォルダの数です。10 より少ないときは、場所のまちがいなどで空に近いバックアップになっているので、失敗として扱います。
手順 D:毎週動くようにする
crontab -e を開いて、最後に次の 1 行を足します。毎週日曜の 3:10 に、サイトを 1 つずつ順に取ります。第 6 回のデータベース(毎晩 2:40)が終わったあとの時刻にしています。
10 3 * * 0 for n in mysite blog; do /home/pi/backup/files_backup.sh $n weekly; done
- 最後の
weeklyのときだけ、取ったあとに新しい 4 本より古いものを消します。サイトの更新の前などに手動で取るときはnowにします。名前がfiles_now_で始まり、古いものを消す処理と「新しい 4 本」の数の外になります(いらなくなったら手動で消します)。 - 失敗したときは、
~/backup/FAILED_FILESのファイルができます。第 6 回のデータベースのFAILEDとは別の名前にして、片方の成功で、もう片方の失敗の記録が消えないようにしています。古いものを消す処理は成功したときしか動かないので、失敗が続いても、手元のよいバックアップは減りません。
バックアップから復元する
1 つのファイルだけ取り出す
消してしまった画像を 1 枚だけ戻したい、というときは、そのファイルだけをラズパイの /tmp に取り出します。
ls /mnt/nas/backup/mysite/
tar tzf /mnt/nas/backup/mysite/files_20261115_0310.tar.gz | grep photo.jpg
mkdir -p /tmp/restore && cd /tmp/restore
tar xzf /mnt/nas/backup/mysite/files_20261115_0310.tar.gz wp-content/uploads/2026/11/photo.jpg
取り出したファイルは、PC にコピーして、サーバーパネルのファイル管理や FTP でサーバーの同じ場所に戻します(ファイル専用の鍵は「出す」ことしかできないので、ラズパイから直接サーバーに書き戻すことはできません)。
サイトをまるごと戻す
テーマやプラグインまで全部を戻すときも、同じ tar.gz を PC で展開して、wp-content をサーバーに上げ直します。データベースは、第 6 回の同じ日付のバックアップを使います。ファイルとデータベースの日付がずれると、記事にはあるのに画像がない、ということが起きるので、なるべく近い日付のものをそろえます。
つまずきやすい所
- ログに「tar: .: file changed as we read it」と出る:取っているあいだに、サイトのファイルが変わったという知らせです。家でも、下の表の参考サイト B(10/28)と参考サイト C(11/10)で出ました。tar はこのとき 1 を返しますが、バックアップは最後まで作られているので、スクリプトは 1 を受け入れて、その後の確認(gzip として読めるか・ファイルの数)で決めます。
- 「知らない名前」:手順 A の dir_of に、その名前を書いていない
- .broken や .part のファイルが残っている:確認で NG になったものや、途中で止まったものです。古いものを消す処理の対象の外なので、原因を見たら手動で消します(大きいサイトは 1 本で数 GB あります)。
- NAS がいっぱいになる:1 サイト × 4 本の大きさが要ります。
df -h /mnt/nasで空きを見て、足りなければ KEEP を減らします。 - とても時間がかかる:画像の多いサイトは数 GB になります。下の表の参考サイト C は 約 2.1GB ありました。大きいサイトは、NAS の上で壊れていないかを確認する時間も長くなります(NAS の性能で変わります)。日曜の夜中に動かすのはこのためです。
実際の大きさと時間
| サイト | 大きさ・ファイル数・時間 |
|---|---|
| 参考サイト A(記事が少ない) | 66MB・9,233 ファイル・43 秒 |
| 参考サイト B(記事と画像が中くらい) | 526MB・17,752 ファイル・280 秒 |
| 参考サイト C(画像が多い) | 約 2.1GB・14,621 ファイル・約 18 分(受け取り約 7 分+確認約 11 分) |
データベース(第 6 回)が数 MB〜数百 MB なのに対して、ファイルはサイトによって 1 けたから 2 けた大きくなります。毎晩ではなく毎週にしているのは、このためです。
分からないときは、このページを AI に渡してください
このページの URL を AI(Claude など)に渡して「この手順を手伝って」と頼めば、一緒に進めてくれます。頼み方の例です。
- 「(このページの URL)の EXCLUDES に、WooCommerce の注文の書類のフォルダも足したい」
- 「(このページの URL)の手順 C で、確認で NG と出た。backup.log はこうだった:(ログを貼る)」
まとめ
この回でできるようになったこと
- 毎週日曜の 3:10 に、wp-content が NAS に保存され、4 週分が残る
- 個人情報が入るフォルダとキャッシュは、
--excludeでバックアップから外せる - 第 6 回のデータベースと合わせて、サイトを丸ごと戻せる
git で管理していない・7 日より前に戻したいことがある、という人は、この仕組みを使ってください。そうでなければ、サーバーの自動のバックアップだけでよいことも多いです。わたしの家は、ファイルはエックスサーバーの 7 日分に任せ、データベースは第 6 回の仕組みでラズパイでも取っています。
次に読む
- 前の回:第 6 回「データベースのバックアップを完全自動化」
- 次の回:第 8 回「ラズパイを有線の LAN でつなぐ」