【2026年7月3日時点の情報です】
このブログは、自宅のサーバーで動いています。サーバー構築の手順もこのブログに書いているので、サーバーが壊れると、直すための手順書まで一緒に読めなくなってしまいます。
そこで、毎日夜中に、データベース、記事を静的なHTMLにしたもの、サーバーの設定ファイルをまとめて、家のNAS(Synology DS224+)に送っています。この記事では、その仕組みと、2026年10月に見直して直したところをまとめます。作業はすべてrootで行っています。
バックアップの全体像
バックアップは、サーバー側のスクリプトとWordPressのプラグイン(UpdraftPlus)の2本立てで、どちらも保存先は家のNASです。
| 時刻 | 何をするか | 保存先 | 残す数 |
|---|---|---|---|
| 毎日 4:00 | データベースをデータベースごとにファイルにする(mariadb-backup.sh) | サーバーの /backup/mariadb | 1 |
| 続けて | 公開中の記事を静的なHTMLにしてZIPにまとめる(wp-html-backup.sh) | サーバーの /backup/wordpress | 1 |
| 続けて | 上の2つと、/home・/root・/var/www・/etc などを1つのファイルにまとめる(backup.sh) | サーバーの /backup/backup.tar.bz2 | 1 |
| 毎日 5:00 | まとめたファイルを日付付きの名前でNASに送る(backup_to_rclone.sh) | NAS(SFTP) | 7日分 |
| 毎日(UpdraftPlusの時刻) | WordPressのファイルとデータベースをバックアップ | NAS(FTP) | 2回分 |
サーバーの中には1日分しか置かず、日付ごとの分はNAS側に残す形です。サーバーのディスクが壊れても、NASに7日分あれば、その中から戻せます。
静的HTMLは、WordPressが動かなくなったときでも、ブラウザで開くだけで記事を読めるようにするためのものです。サーバーを直す手順を、壊れたサーバーの中の記事から探せるようにしています。
1. WP-CLIを入れる
静的HTMLを作るときに、公開中の記事のURLの一覧をWordPressから取り出すため、コマンドでWordPressを操作できるWP-CLIを入れます。
[root@almalinux ~]# curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
[root@almalinux ~]# chmod +x wp-cli.phar
[root@almalinux ~]# mv wp-cli.phar /usr/local/bin/wp
[root@almalinux ~]# wp --info --allow-root ※バージョンなどが表示されればOK2. データベースのバックアップ(mariadb-backup.sh)
データベースを、データベースごとにSQLのファイルにします。
大事なのは、スクリプトにMariaDBのrootのパスワードを書かないことです。私は以前、パスワードをスクリプトにそのまま書いていましたが、ファイルを見られるとデータベースを全部操作できてしまいます。そこで、MariaDBのrootを「サーバーのrootからはパスワードなしで入れる(unix_socket)、ほかからはパスワードで入る」設定にしました。
[root@almalinux ~]# mysql -u root -p -e "ALTER USER 'root'@'localhost' IDENTIFIED VIA unix_socket OR mysql_native_password USING PASSWORD('新しいパスワード');"
Enter password: ※今のパスワードを入力
[root@almalinux ~]# mysql -e "SELECT CURRENT_USER();" ※パスワードなしで入れるか確認cronはサーバーのrootで動くので、この設定ならスクリプトにパスワードがなくてもバックアップが取れます。phpMyAdminなどからrootで入るときは、新しいパスワードを使います。
スクリプトは次のとおりです。
[root@almalinux ~]# vi /root/mariadb-backup.sh
#!/bin/bash
PATH=/usr/local/sbin:/usr/bin:/bin
# バックアップ先ディレクトリ
BACKDIR=/backup/mariadb
# バックアップ先ディレクトリを作り直す
rm -rf $BACKDIR
mkdir -p $BACKDIR
# データベース名を取得
DBLIST=`ls -p /var/lib/mysql | grep / | tr -d /`
# データベースごとにバックアップ(パスワードは書かず、unix_socketで接続)
for dbname in $DBLIST
do
[ $dbname = "performance_schema" ] && continue
table_count=`mysql -u root -B -e "show tables" $dbname|wc -l`
[ $table_count -ne 0 ] &&
mysqldump --events -u root $dbname > $BACKDIR/$dbname.sql
done
[root@almalinux ~]# chmod 700 /root/mariadb-backup.sh
[root@almalinux ~]# /root/mariadb-backup.sh && ls -lh /backup/mariadb/ ※手で動かして確認wordpress.sql などのファイルが今の時刻で作られていれば成功です。
3. 記事を静的HTMLにする(wp-html-backup.sh)
公開中の記事と固定ページのURLをWP-CLIで取り出し、wgetで1ページずつHTMLとして保存して、ZIPにまとめます。
[root@almalinux ~]# vi /root/wp-html-backup.sh
#!/bin/bash
# エラーが起きたらすぐに止める
set -e
# 1. 公開中のURLの一覧(list.txt)を作る
cd /var/www/html
/usr/local/bin/wp post list --post_type=post,page --post_status=publish --field=url --allow-root > /backup/wordpress/list.txt
# 2. URLのドメインを、サーバー自身(http://127.0.0.1/)に置き換える
sed -i 's|https://softdev-learn.cc/|http://127.0.0.1/|g' /backup/wordpress/list.txt
# 3. サーバーの中から直接ページを取得する
# (Cloudflareを通らないように127.0.0.1に送り、Hostヘッダーでドメインを伝える)
#(-q で進み具合を出さない。失敗したときだけ、標準エラーに出して止まる → cron がメールで知らせる)
cd /backup/wordpress
HTTPS=on wget -q --page-requisites --adjust-extension --convert-links --no-parent \
--header="Host: softdev-learn.cc" \
-i list.txt || { rc=$?; echo "wp-html-backup.sh: wget failed (code ${rc})" >&2; exit ${rc}; }
# 4. 新しい名前でZIPを作り、前回のZIPと入れ替える(前回のZIPとURLの一覧は除く)
zip -q -r wordpress_html_backup.new.zip ./* -x "*.zip" -x "list.txt"
mv -f wordpress_html_backup.new.zip wordpress_html_backup.zip
# 5. 取得したフォルダとURLの一覧を消す
rm -rf ./127.0.0.1 ./list.txt
logger -t wp-html-backup.sh "html backup ended."
[root@almalinux ~]# chmod 700 /root/wp-html-backup.shset -e:
途中のコマンドが失敗したら、そこでスクリプトを止めるwp post list … --field=url:
WP-CLI で、公開中の記事と固定ページの URL だけを一覧にして、list.txtに書くsed -i 's|https://softdev-learn.cc/|http://127.0.0.1/|g':
一覧の URL のドメインを、サーバー自身に置き換えるHTTPS=on wget -q --page-requisites --adjust-extension --convert-links --no-parent:
ページと、表示に要る画像や CSS を取得し、HTML の拡張子を付け、ページの中のリンクを手元で開ける形に直す。-qは、進み具合を画面に出さない--header="Host: softdev-learn.cc":
127.0.0.1 に送っても、WordPress にどのサイトあてかを伝える-i list.txt || { rc=$?; echo "…" >&2; exit ${rc}; }:list.txtの URL を順に取得する。||の右は、wgetが失敗したときだけ動く。戻り値をrcに取っておき、エラーの出口(標準エラー)に1行書いて、同じ戻り値で止まる。C でいえばif (ret != 0) { fprintf(stderr, …); exit(ret); }zip -q -r … -x "*.zip" -x "list.txt":
取得したものを、新しい名前の ZIP に一から作る。前回の ZIP と URL の一覧は入れない。-q は、入れたファイルの一覧を画面に出さないmv -f wordpress_html_backup.new.zip wordpress_html_backup.zip:
作り終わった ZIP を、前回の ZIP の名前に入れ替える。作っている途中で失敗したときは、set -eでここまで来ないので、前回の ZIP がそのまま残るrm -rf ./127.0.0.1 ./list.txt:
取得したフォルダと URL の一覧を消すlogger -t wp-html-backup.sh "html backup ended.":
最後まで動いたことを、システムの記録に名前を付けて1行残す(8で確かめる)chmod 700 /root/wp-html-backup.sh:
root だけが読み書き・実行できるようにする
URLを127.0.0.1に置き換えているのは、ドメインのまま取得すると、サーバーから一度インターネットに出てCloudflareを通ることになり、時間がかかったり、Cloudflareのルールに止められたりするためです。
set -e があるので、途中のコマンドが失敗すると、このスクリプトはそこで止まります。wget は、記事の中の画像が1つでも見つからない(404)と失敗を返すので、そのときは「wp-html-backup.sh: wget failed (code 8)」の1行が root 宛てのメールで届きます。あとで説明するcronの設定では、ここで止まっても次のバックアップが動くようにしています。
毎朝 cron のメールが届いていたので、静かにした(2026年10月)
このスクリプトを入れてから、毎朝4時ごろに root 宛てのメールが届いていました。中身は、wget の進み具合と、zip の「adding: …」の行です。cron は、スクリプトが画面に出したものを、メールで送ってきます。失敗していなくても毎日届くので、本当に失敗したときのメールに気づけなくなります。そこで、成功したときは何も出さず、失敗したときだけ知らせる形に直しました。
| No. | 直したところ | 直し方 | 結果 |
|---|---|---|---|
| 1 | wget | -q を付け、失敗したときだけ、エラーの出口に1行書いて止まる | 成功なら静か。取得に失敗したらメールが来る |
| 2 | zip | -q を付ける | 「adding: …」の行が出なくなる |
| 3 | いちばん最後 | logger で1行残す | メールは来ないが、最後まで動いたことを記録で確かめられる |
直す前に控えを取り、直したあとに、書き方の誤りがないかと、変わった行を確かめました。
[root@almalinux ~]# cp -p /root/wp-html-backup.sh /root/wp-html-backup.sh.20261031
[root@almalinux ~]# vi /root/wp-html-backup.sh
[root@almalinux ~]# bash -n /root/wp-html-backup.sh && echo "syntax OK"
syntax OK
[root@almalinux ~]# diff /root/wp-html-backup.sh.20261031 /root/wp-html-backup.sh
14a15
> #(-q で進み具合を出さない。失敗したときだけ、標準エラーに出して止まる → cron がメールで知らせる)
16c17
< HTTPS=on wget --page-requisites --adjust-extension --convert-links --no-parent \
---
> HTTPS=on wget -q --page-requisites --adjust-extension --convert-links --no-parent \
18c19
< -i list.txt
---
> -i list.txt || { rc=$?; echo "wp-html-backup.sh: wget failed (code ${rc})" >&2; exit ${rc}; }
21c22
< zip -r wordpress_html_backup.zip ./* -x "*.zip" -x "list.txt"
---
> zip -q -r wordpress_html_backup.zip ./* -x "*.zip" -x "list.txt"
24a26,27
>
> logger -t wp-html-backup.sh "html backup ended."cp -p … .20261031:
直す前のスクリプトを、日付を付けた名前で残す。-pは持ち主・権限・日付もそのまま写すvi /root/wp-html-backup.sh:
上のスクリプトのとおりに直すbash -n …:
スクリプトを動かさずに、書き方の誤りだけを調べる(C でいえば、コンパイルだけ通す)diff 控え 新しいほう:
変わった行だけを出す。<が前の行、>が新しい行
変わったのは、足したコメント1行と、wget・zip・最後の logger の行だけです。続けて、cron と同じく root で一度動かしました。
[root@almalinux ~]# /root/wp-html-backup.sh; echo "rc=$?"
rc=0
[root@almalinux ~]# journalctl -t wp-html-backup.sh --since "15 min ago" --no-pager
10月 31 10:17:59 myserver wp-html-backup.sh[125108]: html backup ended./root/wp-html-backup.sh; echo "rc=$?":
直したスクリプトを動かし、終わったら戻り値を出す。rc=0なら最後まで正常に終わったjournalctl -t wp-html-backup.sh --since "15 min ago" --no-pager:
システムの記録から、loggerで残した行だけを、ここ15分の分だけ出す
動いている間、画面には何も出ず、rc=0 と記録の1行だけが出ました。次の朝は、4時の cron のメールが来ず、記録には「11月 01 04:01:02 … html backup ended.」が残っていました。
2026年11月には、ZIP の作り方も直しました。前は前回の ZIP に中身を足していく作りだったので、下書きに戻した記事や消した記事の HTML も、ZIP の中に残り続けていました。今は、新しい名前で ZIP を一から作ってから、前回の ZIP と入れ替えています(上のスクリプトの「# 4.」の下の2行)。
4. サーバー全体を1つのファイルにまとめる(backup.sh)
/root/backuplist に書いたフォルダを rsync で集めて、/backup/backup.tar.bz2 の1つのファイルにまとめます。失敗したときは、root宛てにメールが届くようにしています(root宛てのメールは、AlmaLinux 9の初期設定でGmailに転送しています)。スクリプトは長いので、ここでは対象のリストだけ載せます。
[root@almalinux ~]# cat /root/backuplist ※バックアップの対象
/home
/root
/var/www
/backup/mariadb
/backup/wordpress
/etc
/usr/local/sbin
[root@almalinux ~]# cat /root/backupnolist ※対象から外すもの
/var/www/error
/var/www/icons
/var/www/manual
/home/devuser/.vscode-server/etc と /usr/local/sbin は、2026年10月に足しました。それまでは、Apache・MariaDB・cronなどの設定ファイルがバックアップに入っておらず、サーバーを作り直すときは、設定を一から思い出すことになっていました。
外すものの /home/devuser/.vscode-server は、2026年10月に足しました。VS Code でサーバーにつなぐと、サーバーの側に VS Code の部品が入り、版が上がるたびに約600MB ずつたまっていきます。VS Code が入れ直せるものなので、バックアップからは外しました。
バックアップには、ブログの全データやユーザーのパスワードのハッシュが入るので、置き場所はrootだけが入れるようにしておきます。
[root@almalinux ~]# chmod 700 /backup5. NASに日付付きで送る(backup_to_rclone.sh)
まとめたファイルを、rcloneでNASに送ります。NASのSFTPを、rcloneに sftp という名前で登録してある前提です(rclone config で登録します)。
送るときに日付付きの名前にして、8日より古いものを消すので、NASには7日分が残ります。
[root@almalinux ~]# vi /root/backup_to_rclone.sh
#!/bin/bash
# バックアップを日付付きの名前でNASに送り、7日より古いものを消す
BACKUPFILE=/backup/backup.tar.bz2
DEST=sftp:/ServerBackup/myserver
rclone copyto "${BACKUPFILE}" "${DEST}/backup-$(date +%Y%m%d).tar.bz2" --ignore-checksum || exit 1
rclone delete "${DEST}" --include "backup-*.tar.bz2" --min-age 8d
exit 0
[root@almalinux ~]# chmod 700 /root/backup_to_rclone.sh
[root@almalinux ~]# /root/backup_to_rclone.sh && rclone lsl sftp:/ServerBackup/myserver ※手で動かして確認
2980346633 2026-10-09 04:15:30.000000000 backup-20261009.tar.bz2以前は、毎日同じ名前(backup.tar.bz2)で上書きしていたので、NASにも前日の分しか残っていませんでした。おかしくなったデータに気づかないまま1日たつと、正常だったときに戻せなくなります。日付を付けて数日分残すようにしたのは、そのためです。私のバックアップは1日あたり約2.3〜2.7GB(2026年10月に .vscode-server を外してから)なので、NASでは約16〜19GBを使います。
6. 毎日自動で動かす(cron)
4時にサーバーの中でバックアップを作り、5時にNASに送ります。
[root@almalinux ~]# vi /etc/cron.d/backup
0 4 * * * root /root/mariadb-backup.sh; /root/wp-html-backup.sh; /root/backup.sh
0 5 * * * root /root/backup_to_rclone.sh以前は、3つのスクリプトを && でつないでいました。&& は「前が成功したら次を動かす」という意味なので、静的HTMLの作成で1ページでも失敗すると、サーバー全体のまとめ(backup.sh)が動かず、NASに送るファイルも古いままになります。; にすると、前が失敗しても次が動きます。
7. もう1本:UpdraftPlus
サーバー側のバックアップとは別に、WordPressのプラグイン「UpdraftPlus」でも、毎日バックアップを取っています。設定は次のとおりです。
| 項目 | 設定 |
|---|---|
| ファイルのバックアップ | 毎日(Daily)、2回分を残す |
| データベースのバックアップ | 毎日(Daily)、2回分を残す |
| 保存先 | FTP(家のNASの /ServerBackup/Wordpress) |
| 対象 | プラグイン、テーマ、アップロード、wp-content のほかのフォルダ |
2本立てにしているのは、保存先も方法も別なので、片方が失敗していても、もう片方で戻せるからです。それに、WordPressだけがおかしくなったときは、UpdraftPlusなら管理画面の「復元」ボタンで戻せるので、こちらのほうが手軽です。サーバーごと作り直すときは、サーバー側のバックアップを使います。
なお、無料版で使えるFTPは暗号化されない通信なので、パスワードとバックアップの中身が、家のLANの中をそのまま流れます。家の中だけの通信なので、私はこのまま使っています。
8. 毎日動いているかの確認
バックアップは、動いていないことに気づきにくいので、ときどき次のコマンドで確かめています。
[root@almalinux ~]# journalctl -t backup.sh --since "3 days ago" --no-pager | grep -E "started|ended|aborted" ※毎日最後まで動いたか
10月 07 04:01:18 myserver backup.sh[1424944]: backup started.
10月 07 04:16:20 myserver backup.sh[1425053]: backup ended.
10月 08 04:01:11 myserver backup.sh[1595673]: backup started.
10月 08 04:16:18 myserver backup.sh[1595788]: backup ended.
10月 09 04:01:09 myserver backup.sh[1726386]: backup started.
10月 09 04:15:32 myserver backup.sh[1726492]: backup ended.
[root@almalinux ~]# journalctl -t wp-html-backup.sh --since today --no-pager ※静的HTMLが最後まで作れたか
11月 01 04:01:02 myserver wp-html-backup.sh[129481]: html backup ended.
[root@almalinux ~]# rclone lsl sftp:/ServerBackup/myserver ※NASに日付ごとに届いているか
[root@almalinux ~]# tar -tjf /backup/backup.tar.bz2 | grep -m3 "/etc/httpd" ※設定ファイルが入っているかjournalctl -t backup.sh --since "3 days ago" --no-pager | grep -E "started|ended|aborted":
backup.sh が残した記録のうち、ここ3日分の始まり・終わり・中断の行だけを出すjournalctl -t wp-html-backup.sh --since today --no-pager:
静的HTMLのスクリプトが今日残した記録を出すrclone lsl sftp:/ServerBackup/myserver:
NAS のフォルダのファイルを、大きさ・日時・名前つきで一覧にするtar -tjf … | grep -m3 "/etc/httpd":
まとめたファイルの中身の一覧から、Apache の設定の行を3つだけ出す
毎日「started」と「ended」が並び、静的HTMLの「html backup ended.」が出ていて、NASに日付付きのファイルが増えていれば、正しく動いています。「aborted」が出ていたり、静的HTMLの記録がなかったりしたら、root宛てのメールで原因を確かめます。静的HTMLのスクリプトは、うまくいった日はメールを出さないので、メールが来ないのがふつうです。
2026年10月に、NASのバックアップをサーバーに取り出して、データベースを試験用のデータベースに入れてみたところ、記事のデータが問題なく読めました。静的HTMLのZIPも、535ファイルがそろっていて開けました。確認が終わったら、試験用のデータベースと取り出したファイルは消しています。
まとめ
自宅サーバーのWordPressを、データベース・静的HTML・サーバーの設定ごと毎日NASに送り、7日分を残すようにしました。2026年10月に見直したときに直したのは、次の5つです。
- スクリプトにMariaDBのパスワードを書かない(unix_socketで接続)
- NASに日付付きで7日分を残す(以前は毎日上書きで1日分だけ)
- /etc と /usr/local/sbin をバックアップの対象に加える
- cronを
&&から;にして、途中で失敗しても次が動くようにする - 静的HTMLのスクリプトを、成功した日は何も出さず、失敗した日だけメールが来る形にする
どれも、設定したときは動いていたので気づかず、見直して初めて分かったことでした。バックアップは、ときどき中身まで確かめておくのがおすすめです。



コメント