シェルスクリプトでtarを使った増分バックアップを実装する方法|フルと差分の組み合わせと世代管理設計

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)シェルスクリプト > シェルスクリプトでtarを使った増分バックアップを実装する方法|フルと差分の組み合わせと世代管理設計
「フルバックアップをcronで毎日動かしていたら、1ヶ月でディスクが溢れた」
現場でよくある失敗だ。フルバックアップは確かに確実だが、ファイルが数GB以上あれば毎日全量を圧縮するのはディスクにもCPUにも優しくない。

この記事では、tar コマンドの --newer-mtime と GNU tar の --listed-incremental を使い、シェルスクリプトで増分・差分バックアップを実装する方法を解説する。週次フルバックアップ+日次差分バックアップの設計パターンと世代管理、cronによる完全自動化、リストア手順まで実践的に紹介する。

この記事のポイント

・tar --newer-mtime で「差分バックアップ」を実装できる
・GNU tar の --listed-incremental で真の増分バックアップが実現する
・週次フル+日次差分の cron 設計でディスク消費を最小化できる
・リストア時はフル→差分の適用順序に注意が必要


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜtarで増分バックアップが必要なのか

rsync を使った世代管理バックアップが主流になっている一方で、tar ベースの増分バックアップには rsync では代替しにくい用途がある。

アーカイブを1ファイルで管理したい場合:tar.gz はファイル1つなのでリモートへの転送・保管が容易。rsync はディレクトリツリーをそのまま扱うため、バックアップ先の構成が複雑になりやすい
圧縮率を上げたい場合:tar.gz・tar.bz2・tar.xz と圧縮形式を選べる。大量のテキストログや HTML ファイルは 80%以上削減できることもある
テープやオブジェクトストレージに流す場合:S3・GCS・Azure Blob など、ファイル単位でアップロードする場合は tar アーカイブが扱いやすい

一方、tar の差分バックアップには設計上の注意点がある。--newer-mtime は「フルバックアップより新しいファイル」を対象にするため、リストア時に「最後のフル+複数の差分」を順番に展開しなければならない。これを理解したうえで設計することが重要だ。

tarの増分バックアップの仕組みを理解する

tar には増分バックアップを実現するアプローチが2つある。

--newer-mtime による差分バックアップ:指定したファイルやタイムスタンプより新しい更新時刻(mtime)を持つファイルだけをアーカイブする。「前回のフルバックアップ以降に変更されたファイル」という意味では「差分バックアップ」に分類される
--listed-incremental によるスナップショット型増分バックアップ:GNU tar が生成するスナップショットファイル(.snar)に前回バックアップ時のファイルリストを記録し、次回は変更差分のみをアーカイブする。リストアも機械的にできるため管理がシンプルになる

それぞれ実装してみよう。動作確認環境は以下の通りだ。

・OS: Rocky Linux 9.4 / RHEL 9.4
・tar: GNU tar 1.34(tar --version で確認)
・バックアップ対象: /var/www/html(Webコンテンツ)

フルバックアップスクリプトを設計する

増分・差分バックアップは必ずフルバックアップとセットで運用する。まずフルバックアップスクリプトを固めよう。

1. バックアップディレクトリの構成

/backup/ ├── full/ # 週次フルバックアップの格納先 ├── diff/ # 日次差分バックアップの格納先 └── snapshot/ # GNU tar スナップショットファイル(listed-incremental用)

2. フルバックアップスクリプト(full-backup.sh)

#!/bin/bash # full-backup.sh — 週次フルバックアップ set -euo pipefail BACKUP_SRC="/var/www/html" BACKUP_DEST="/backup/full" DATE=$(date +%Y%m%d) LOG="/var/log/backup.log" log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOG"; } mkdir -p "$BACKUP_DEST" ARCHIVE="${BACKUP_DEST}/full_${DATE}.tar.gz" if [ -f "$ARCHIVE" ]; then log "本日分のフルバックアップは既に存在する: $ARCHIVE" exit 0 fi log "フルバックアップ開始: $ARCHIVE" tar czf "$ARCHIVE" \ --exclude="${BACKUP_SRC}/tmp" \ --exclude="${BACKUP_SRC}/cache" \ "$BACKUP_SRC" SIZE=$(du -sh "$ARCHIVE" | cut -f1) log "フルバックアップ完了: $ARCHIVE ($SIZE)" # 30日以上前のフルバックアップを削除 find "$BACKUP_DEST" -name "full_*.tar.gz" -mtime +30 -delete log "古いフルバックアップを削除"

スクリプトのポイントを確認しておこう。

set -euo pipefail:コマンドエラーで即座にスクリプトを停止する安全設計
DATE=$(date +%Y%m%d):ファイル名に日付を埋め込む。既存チェックと削除ルールを日付で管理できる
--exclude:tmp・cache など不要なディレクトリを除外してアーカイブサイズを削減する
find -mtime +30 -delete:30日超えたファイルを自動削除して世代管理する

--newer-mtimeで差分バックアップスクリプトを設計する

1. 差分バックアップのしくみ

--newer-mtime=FILE は、指定した FILE よりも mtime(最終更新時刻)が新しいファイルだけをアーカイブする。

# 動作確認: フルバックアップファイルより新しいファイルを表示 tar czf /dev/null --newer-mtime="/backup/full/full_20260817.tar.gz" \ /var/www/html --verbose 2>&1 | head -20

--newer-mtime のアーカイブ対象は「ファイルの mtime が指定ファイルの mtime より新しいもの」だ。フルバックアップファイル自体のタイムスタンプを基準にすることで、「フルバックアップ実行後に変更されたファイル」だけを拾える。

注意点:ファイルが削除された場合、差分バックアップには何も記録されない。削除されたファイルをリストアで再現したい場合は後述の --listed-incremental を使うこと。

2. 差分バックアップスクリプト(diff-backup.sh)

#!/bin/bash # diff-backup.sh — 日次差分バックアップ(--newer-mtime方式) set -euo pipefail BACKUP_SRC="/var/www/html" FULL_DEST="/backup/full" DIFF_DEST="/backup/diff" DATE=$(date +%Y%m%d_%H%M) LOG="/var/log/backup.log" log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOG"; } mkdir -p "$DIFF_DEST" # 最新のフルバックアップファイルを取得 LAST_FULL=$(ls -t "${FULL_DEST}"/full_*.tar.gz 2>/dev/null | head -1) if [ -z "$LAST_FULL" ]; then log "エラー: フルバックアップが見つかりません。先にフルバックアップを実行してください" exit 1 fi ARCHIVE="${DIFF_DEST}/diff_${DATE}.tar.gz" log "差分バックアップ開始 (基準: $(basename $LAST_FULL))" tar czf "$ARCHIVE" \ --newer-mtime="$LAST_FULL" \ --exclude="${BACKUP_SRC}/tmp" \ --exclude="${BACKUP_SRC}/cache" \ "$BACKUP_SRC" # 差分が空の場合(変更なし)もファイルが生成されるため確認 SIZE=$(du -sh "$ARCHIVE" | cut -f1) log "差分バックアップ完了: $ARCHIVE ($SIZE)" # 14日以上前の差分バックアップを削除 find "$DIFF_DEST" -name "diff_*.tar.gz" -mtime +14 -delete log "古い差分バックアップを削除"

実行結果の例を確認しよう(実機での出力)。

$ /usr/local/bin/diff-backup.sh 2026-08-21 01:00:05 差分バックアップ開始 (基準: full_20260817.tar.gz) 2026-08-21 01:00:18 差分バックアップ完了: /backup/diff/diff_20260821_0100.tar.gz (4.2M) 2026-08-21 01:00:18 古い差分バックアップを削除 $ ls -lh /backup/diff/ total 4.2M -rw-r--r-- 1 root root 4.2M Aug 21 01:00 diff_20260821_0100.tar.gz

フルバックアップが 320MB であるのに対し、差分バックアップは 4.2MB に抑えられている。変更ファイルが少ない日常運用では大幅なディスク節約になる。

GNU tarのlisted-incrementalで真の増分バックアップを実装する

シェルスクリプトのバックアップ設計をさらに深めたい方は、シェルスクリプト実践ガイド も合わせて確認してほしい。

1. --listed-incrementalとは

GNU tar の --listed-incremental=FILE(略記: -g FILE)は、スナップショットファイルに前回バックアップ時のディレクトリ情報を記録する仕組みだ。

スナップショットファイルが存在しない → フルバックアップとして動作し、スナップショットを生成
スナップショットファイルが存在する → 前回以降に変更・追加されたファイルのみをアーカイブ(増分)

--newer-mtime との最大の違いは「削除されたファイルの情報もスナップショットに記録される」点だ。リストア時に --listed-incremental を付けて展開すると、削除されたファイルを自動的に消去しながら正確なリストアができる。

2. 増分バックアップスクリプト(incremental-backup.sh)

#!/bin/bash # incremental-backup.sh — listed-incremental型増分バックアップ set -euo pipefail BACKUP_SRC="/var/www/html" INCR_DEST="/backup/incremental" SNAPSHOT="/backup/snapshot/www_snapshot.snar" DATE=$(date +%Y%m%d_%H%M) LOG="/var/log/backup.log" log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOG"; } mkdir -p "$INCR_DEST" "$(dirname $SNAPSHOT)" # スナップショットが存在しない場合はフルバックアップ if [ ! -f "$SNAPSHOT" ]; then MODE="フル" ARCHIVE="${INCR_DEST}/full_${DATE}.tar.gz" else MODE="増分" ARCHIVE="${INCR_DEST}/incr_${DATE}.tar.gz" fi log "${MODE}バックアップ開始: $ARCHIVE" tar czf "$ARCHIVE" \ --listed-incremental="$SNAPSHOT" \ --exclude="${BACKUP_SRC}/tmp" \ --exclude="${BACKUP_SRC}/cache" \ "$BACKUP_SRC" SIZE=$(du -sh "$ARCHIVE" | cut -f1) log "${MODE}バックアップ完了: $ARCHIVE ($SIZE)"

3. 週次でスナップショットをリセットしてフルバックアップに戻す設計

増分が積み重なりすぎるとリストア時に多くのアーカイブを順番に展開しなければならなくなる。週次でスナップショットファイルを削除して強制フルバックアップに戻す設計がおすすめだ。

#!/bin/bash # weekly-full-reset.sh — スナップショットをリセットして強制フルバックアップ SNAPSHOT="/backup/snapshot/www_snapshot.snar" INCR_DEST="/backup/incremental" LOG="/var/log/backup.log" log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOG"; } # スナップショットを削除(次回実行時にフルバックアップとして動作) if [ -f "$SNAPSHOT" ]; then rm -f "$SNAPSHOT" log "スナップショットをリセット。次回実行でフルバックアップを取得" fi # 30日以上前のアーカイブを削除 find "$INCR_DEST" -name "*.tar.gz" -mtime +30 -delete log "古い増分バックアップを削除" # フルバックアップを実行 /usr/local/bin/incremental-backup.sh

cronで週次フル+日次差分を自動化する

2つの方式(--newer-mtime と --listed-incremental)のどちらを選ぶにしても、cron の設計パターンは同様だ。

/etc/cron.d/backup に設定する例を示す。

# /etc/cron.d/backup — バックアップジョブ定義 SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin MAILTO=admin@example.com # 週次フルバックアップ: 毎週日曜日 0:00(--newer-mtime方式の場合) 0 0 * * 0 root /usr/local/bin/full-backup.sh >> /var/log/backup.log 2>&1 # 日次差分バックアップ: 月曜~土曜 1:00 0 1 * * 1-6 root /usr/local/bin/diff-backup.sh >> /var/log/backup.log 2>&1 # 週次スナップショットリセット+フル: listed-incremental方式の場合はこちら # 0 0 * * 0 root /usr/local/bin/weekly-full-reset.sh >> /var/log/backup.log 2>&1 # 0 1 * * 1-6 root /usr/local/bin/incremental-backup.sh >> /var/log/backup.log 2>&1

cron 設定後、次のコマンドで cron ログを確認して正常に実行されているか確認する。

# cron の実行ログを確認 grep CRON /var/log/messages | tail -20 # バックアップログを確認 tail -50 /var/log/backup.log

バックアップの検証とリストア手順

1. バックアップファイルの内容確認

アーカイブが正常に作成されているかを -t(--list)オプションで確認する。tar の基本的な使い方については tar コマンドの実用例 も参照してほしい。

# アーカイブの内容を一覧表示 tar tzf /backup/full/full_20260817.tar.gz | head -20 # ファイル数を確認 tar tzf /backup/full/full_20260817.tar.gz | wc -l # 差分バックアップの内容確認(変更されたファイルが含まれているか) tar tzf /backup/diff/diff_20260821_0100.tar.gz

実機での実行結果の例は以下の通りだ。

$ tar tzf /backup/full/full_20260817.tar.gz | head -5 var/www/html/ var/www/html/index.html var/www/html/wp-config.php var/www/html/wp-content/ var/www/html/wp-content/themes/ $ tar tzf /backup/full/full_20260817.tar.gz | wc -l 4827 $ tar tzf /backup/diff/diff_20260821_0100.tar.gz var/www/html/wp-content/uploads/2026/08/photo-001.jpg var/www/html/wp-content/uploads/2026/08/photo-002.jpg var/www/html/wp-content/cache/db/

2. フルバックアップからリストア

# リストア先ディレクトリを作成 mkdir -p /restore # フルバックアップを展開(アーカイブ内のパスが var/www/html/ なので / を指定) tar xzf /backup/full/full_20260817.tar.gz -C /

注意点:tar は展開先のファイルを上書きするため、本番環境への直接展開は慎重に行うこと。まず -C /restore で別ディレクトリに展開して内容を確認してから置き換える手順が安全だ。

3. フル+差分の段階的リストア(--newer-mtime方式)

# 手順1: フルバックアップを展開 tar xzf /backup/full/full_20260817.tar.gz -C /restore/ # 手順2: 差分バックアップを展開(フルより新しいファイルで上書き) tar xzf /backup/diff/diff_20260818_0100.tar.gz -C /restore/ tar xzf /backup/diff/diff_20260819_0100.tar.gz -C /restore/ tar xzf /backup/diff/diff_20260820_0100.tar.gz -C /restore/ tar xzf /backup/diff/diff_20260821_0100.tar.gz -C /restore/

適用は必ず「フル→古い差分→新しい差分」の順番で行う。逆順や途中をスキップするとリストアが不完全になる。

4. listed-incremental方式のリストア

# リストア時は --listed-incremental=/dev/null でスナップショットを無視して展開 # 手順1: フルバックアップを展開 tar xzf /backup/incremental/full_20260817_0000.tar.gz \ --listed-incremental=/dev/null \ -C /restore/ # 手順2: 増分バックアップを順番に展開(削除されたファイルも自動処理される) tar xzf /backup/incremental/incr_20260818_0100.tar.gz \ --listed-incremental=/dev/null \ -C /restore/ tar xzf /backup/incremental/incr_20260819_0100.tar.gz \ --listed-incremental=/dev/null \ -C /restore/

--listed-incremental=/dev/null を指定することで、展開時はスナップショットファイルを参照せずに展開だけを行う。これが listed-incremental 方式のリストアの定石だ。

tar の解凍操作の基本は tar.bz2 の解凍方法 にまとめているのであわせて確認してほしい。

トラブルシュート

「--newer-mtime: Cannot stat: No such file or directory」が出る

--newer-mtime に指定したファイルが存在しない場合に発生する。フルバックアップが1件もない状態で差分バックアップを実行しようとしたときに多い。

# 原因確認: フルバックアップディレクトリの内容 ls -lh /backup/full/ # 対処: フルバックアップが存在しない場合はスクリプトで先に確認する LAST_FULL=$(ls -t /backup/full/full_*.tar.gz 2>/dev/null | head -1) if [ -z "$LAST_FULL" ]; then echo "エラー: フルバックアップがありません" exit 1 fi

差分バックアップのサイズが毎回フルと変わらない

差分バックアップが期待通りに小さくならない場合、以下を確認する。

フルバックアップの mtime が更新されていないか:フルバックアップの実行直後に touch などでタイムスタンプを変更した場合、--newer-mtime の基準がずれる。スクリプトで touch を使わない設計にする
バックアップ対象のファイルがすべて更新されている:ログファイルや定期的に更新されるキャッシュを除外していない。--exclude の設定を見直す
ファイルシステムの mtime 精度:NFSマウントしたディレクトリなど、mtime 精度が秒単位になっている場合に差分が正確に取れないことがある

「tar: Cannot open: Permission denied」が出る

バックアップ対象ディレクトリへの読み取り権限がない、またはバックアップ保存先への書き込み権限がない場合に発生する。

# バックアップスクリプトを root で実行しているか確認 id # バックアップ先のパーミッション確認 ls -la /backup/ # 必要であればバックアップ先の権限を設定 mkdir -p /backup/{full,diff,incremental,snapshot} chmod 750 /backup /backup/full /backup/diff /backup/incremental /backup/snapshot chown root:root /backup /backup/full /backup/diff /backup/incremental /backup/snapshot

本記事のまとめ

tar を使った増分・差分バックアップの設計パターンをまとめる。
バックアップ方式 コマンド リストアの複雑さ 用途
フルバックアップ tar czf archive.tar.gz /src 低(1ファイルのみ展開) 週次・基準点の作成
差分バックアップ tar czf archive.tar.gz --newer-mtime=last_full /src 中(フル+複数差分を順番に展開) 日次・削除追跡不要の場合
増分バックアップ tar czf archive.tar.gz --listed-incremental=snapshot.snar /src 中(フル+増分を順番に展開) 日次・削除ファイルも追跡したい場合
設計の選択指針をまとめると:

・削除ファイルのリストアが不要で設計をシンプルにしたい → --newer-mtime 方式
・削除されたファイルも正確にリストアしたい → --listed-incremental 方式
・週次でスナップショットをリセットして増分の連鎖を断ち切る設計が運用しやすい
・cron では /etc/cron.d/ に配置して MAILTO で失敗通知を受け取る設定を必ず入れること

フルバックアップと差分・増分を組み合わせることで、ディスク使用量を抑えながら確実な復旧手段を確保できる。まず --newer-mtime 方式から試して運用感を掴み、削除追跡が必要になったら --listed-incremental に移行するとよい。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
シェルスクリプト講座を見る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。