2026年7月29日

2026年7月29日

tarコマンドで定期バックアップするシェルスクリプト:DBダンプ・検証・世代削除・cron設定まで

tarで定期バックアップを取るには、サイトのファイルを tar -czf で1つのアーカイブにまとめ、データベースは mysqldump で別に書き出し、作ったファイルを検証してから古い世代を消す、という流れを1本のシェルスクリプトにしてcronで実行します。この記事では、バックアップで使うtarのオプション、そのまま使えるスクリプトの例と各行の意図、cronでの自動化、GNU tarの増分バックアップ、そして見落とされがちな復元のテストまでを説明します。

バックアップで使うtarのオプション

tar -czf backup.tar.gz -C /var/www html
  • -c: アーカイブを作ります(展開は -x、中身の一覧は -t)。
  • -z: gzipで圧縮します。-j はbzip2、-J はxz、--zstd はzstdです。
  • -f ファイル名: 作るアーカイブの名前です。-f の直後にファイル名を置きます。
  • -C ディレクトリ: 指定したディレクトリに移動してからまとめます。

-C /var/www html と書くと、アーカイブの中のパスは html/... になります。tar -czf backup.tar.gz /var/www/html のように書いても、GNU tarは先頭の / を取り除いて var/www/html/... として保存しますが、-C を使って中のパスを短くしておくほうが、復元先を選ぶときに扱いやすくなります。

圧縮方式の選び方

方式 オプション 特徴
gzip -z どの環境でも展開でき、速度と圧縮率のバランスがよい
xz -J 圧縮率は高いが、圧縮に時間とCPUを使う
zstd --zstd 速度と圧縮率のバランスがよく、マルチスレッドにも対応

WordPressのサイトでは、容量の大部分を画像などのすでに圧縮されたファイルが占めるため、圧縮方式を変えても全体の大きさはあまり変わらないことが多いです。迷ったらgzipで十分です。--zstd はGNU tar 1.31以降で使えるオプションなので、古い環境では tar --version で確認してください(2026年10月現在)。

除外の指定は対象より前に書く

tar -czf backup.tar.gz -C /var/www \
  --exclude='html/wp-content/cache' \
  --exclude='*.log' \
  html

GNU tarの比較的新しい版では、--exclude は「それより後ろに書いた対象」にだけ効く扱いになっていて、対象の後ろに書くと警告が出て効かないことがあります。除外の指定は、まとめる対象(ここでは html)より前に書きます。

バックアップスクリプトの例

WordPressのサイトを想定し、ファイルとデータベースを日付付きで保存し、14日より古いものを削除するスクリプトです。実際のパスとデータベース名に置き換えて使ってください。

#!/bin/bash
# /usr/local/bin/tar-backup.sh
set -euo pipefail
umask 077
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

SITE_PARENT="/var/www"
SITE_NAME="html"
DB_NAME="wordpress"
BACKUP_DIR="/backup/archives"
KEEP_DAYS=14
STAMP="$(date +%F_%H%M)"
ARCHIVE="$BACKUP_DIR/site_$STAMP.tar.gz"
DUMP="$BACKUP_DIR/db_$STAMP.sql.gz"

mkdir -p "$BACKUP_DIR"

# 1. データベースを書き出す
mysqldump --defaults-extra-file=/root/.backup.my.cnf \
  --single-transaction --quick --no-tablespaces \
  --default-character-set=utf8mb4 "$DB_NAME" | gzip > "$DUMP"

# 2. ファイルをまとめる(終了コード 1 は「読み込み中に変更されたファイルがあった」)
set +e
tar -czf "$ARCHIVE" -C "$SITE_PARENT" \
  --exclude="$SITE_NAME/wp-content/cache" \
  --warning=no-file-changed \
  "$SITE_NAME"
rc=$?
set -e
if [ "$rc" -gt 1 ]; then
  echo "tar failed: exit $rc" >&2
  rm -f "$ARCHIVE"
  exit "$rc"
fi

# 3. できたファイルを検証する
gzip -t "$DUMP"
tar -tzf "$ARCHIVE" > /dev/null
( cd "$BACKUP_DIR" && sha256sum "site_$STAMP.tar.gz" "db_$STAMP.sql.gz" > "SHA256SUMS_$STAMP" )

# 4. 古い世代を削除する
find "$BACKUP_DIR" -maxdepth 1 -type f \
  \( -name 'site_*.tar.gz' -o -name 'db_*.sql.gz' -o -name 'SHA256SUMS_*' \) \
  -mtime +"$KEEP_DAYS" -delete

echo "backup completed: $STAMP"

各部分の意図

  • **set -euo pipefail**: 途中のコマンドが失敗したら止めます。pipefail が無いと、mysqldump | gzip で mysqldump が失敗しても gzip の成功だけを見て先へ進み、中身の無いダンプを正常なバックアップとして残してしまいます。
  • **umask 077**: 作ったファイルを本人(root)だけが読めるようにします。アーカイブにはデータベースのパスワードが書かれた wp-config.php が含まれ、ダンプには会員の個人情報が含まれることもあります。
  • **PATH の指定**: cronから実行すると環境変数が最小限になり、コマンドが見つからないことがあります。
  • tarの終了コード: GNU tarでは、0 が成功、1 は「アーカイブ中にファイルが変更された」などの軽微な差異、2 は致命的なエラーです。稼働中のサイトではキャッシュやログが書き換わって 1 になることがあるため、ここでは 2 以上を失敗にしています。
  • 検証: gzip -t で圧縮データが壊れていないか、tar -tzf でアーカイブを最後まで読めるかを確かめます。sha256sum の記録は、別の場所へコピーしたあとに sha256sum -c で破損を確かめるのに使います。
  • 世代の削除: 作ったファイルの更新日時は作成時刻なので、find -mtime で古いものを選べます。削除の条件を名前のパターンで絞っておくと、同じ場所にあるほかのファイルを誤って消しません。

データベースの接続情報

パスワードをスクリプトやコマンドラインに書くと、プロセスの一覧などから見えてしまいます。接続情報は別のファイルに置き、chmod 600 にします。

# /root/.backup.my.cnf
[client]
user=backup
password=your-db-password

バックアップ専用のデータベースユーザーを作り、読み取りに必要な権限(SELECT、SHOW VIEW、TRIGGER、LOCK TABLES など)だけを与えておくと、漏れたときの影響を抑えられます。--no-tablespaces は、MySQL 8.0の一部の版から必要になった PROCESS 権限を求められないようにするための指定です。MariaDBでは mysqldump の代わりに mariadb-dump が使われる環境もあります。

WP-CLIが使える環境なら、sudo -u www-data wp db export - --path=/var/www/html | gzip > ... のように wp db export で書き出すこともできます。この方法なら wp-config.php の接続情報が使われます。

cronで定期実行する

sudo chmod 700 /usr/local/bin/tar-backup.sh
sudo crontab -e
15 3 * * * flock -n /run/tar-backup.lock /usr/local/bin/tar-backup.sh >> /var/log/tar-backup.log 2>&1

毎日3時15分に実行する例です。flock -n は、前回の実行が終わっていないときに次の実行を重ねないための指定です。最初はcronに登録する前に手で実行し、ログと作られたファイルを確認します。

自動のバックアップは、失敗しても誰も気づかないまま何週間も過ぎることがあります。ログを定期的に見るか、スクリプトの最後で監視サービスに完了を知らせる仕組みを入れておくと、止まったことに気づけます。

GNU tarの増分バックアップ

サイトが大きく、毎日のフルバックアップに時間や容量がかかる場合は、GNU tarの --listed-incremental で増分バックアップにできます。前回の状態を記録したファイル(スナップショットファイル、拡張子は .snar がよく使われます)を指定すると、2回目以降は変更されたファイルだけがアーカイブに入ります。

# 週に1回: スナップショットファイルを作り直してフルバックアップ(レベル0)
rm -f /backup/inc/site.snar
tar -czf /backup/inc/full_$(date +%F).tar.gz --listed-incremental=/backup/inc/site.snar -C /var/www html

# 毎日: 同じスナップショットファイルで増分バックアップ
tar -czf /backup/inc/inc_$(date +%F).tar.gz --listed-incremental=/backup/inc/site.snar -C /var/www html

同じスナップショットファイルを使い続けると、毎回「前回から変わった分」の増分になります。フルバックアップを基準にした差分にしたい場合は、レベル0の直後にスナップショットファイルを別名で保存しておき、毎回それをコピーしてから実行します。

復元するときは、フル、増分の順に、空のディレクトリへ展開します。展開時の --listed-incremental には /dev/null を指定します。増分のアーカイブには削除されたファイルの情報も入っているため、展開すると、その時点で存在しなかったファイルが展開先から消されます。既存のディレクトリへ直接展開しないでください。

mkdir -p /restore/test
tar -xzf /backup/inc/full_2026-10-11.tar.gz --listed-incremental=/dev/null -C /restore/test
tar -xzf /backup/inc/inc_2026-10-12.tar.gz  --listed-incremental=/dev/null -C /restore/test

別の場所へ保管し、復元を試す

同じサーバーの中だけにバックアップを置いていると、ディスクの故障やサーバーの乗っ取りで、サイトとバックアップを同時に失います。作ったアーカイブは、別のサーバーやクラウドのストレージへもコピーします。外部に置く場合は、暗号化してから送ると安心です。

そして最も大事なのが、復元のテストです。バックアップは、戻せることを確かめて初めて意味を持ちます。月に1回など定期的に、別の場所へ展開してみます。

mkdir -p /tmp/restore-test
tar -xzf /backup/archives/site_2026-10-17_0315.tar.gz -C /tmp/restore-test
ls /tmp/restore-test/html/wp-config.php
gunzip -c /backup/archives/db_2026-10-17_0315.sql.gz | head -n 20

可能なら、テスト用のサーバーやローカル環境でダンプを読み込み、サイトが表示されるところまで確かめます。本番のデータベースへ誤って読み込まないよう、読み込み先のデータベース名はよく確認してください。

まとめ

  • tarは -czf でまとめ、-C でアーカイブ内のパスを短くします。--exclude はまとめる対象より前に書きます。
  • データベースはtarではなく mysqldump などで書き出します。pipefail を付けないと、ダンプの失敗を見逃します。
  • スクリプトでは umask 077、tarの終了コード(1は軽微、2は致命的)、gzip -t と tar -tzf による検証、名前で絞った世代の削除を押さえます。
  • cronでは PATH と flock に気をつけ、失敗に気づける仕組みを用意します。
  • GNU tarの --listed-incremental で増分バックアップもできますが、復元は空のディレクトリへ順に展開します。別の場所への保管と、定期的な復元テストも欠かせません。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲