rsync 差分同期 実践 - バックアップとミラーリングの定石

rsync 差分同期 実践 - バックアップとミラーリングの定石

この記事で解決できること

  • rsync の差分同期を バックアップ・ミラーリングの実務 で使えるようになる
  • --delete / --link-dest / --exclude正しい使い方と事故防止 が分かる
  • 「同期したつもりが消えた」「容量が足りない」などの 定番トラブルを避ける型 が身につく

結論(実務の型)

  • 片方向の最新化rsync -av src/ dst/
  • 完全ミラー(不要ファイルも削除)rsync -av --delete src/ dst/
  • 世代を残す増分バックアップ--link-dest で前回分をハードリンク
  • 破壊的操作の前は 必ず --dry-run で確認する

前提(対象環境)

  • OS:Ubuntu(rsync 3.x 系)
  • ローカル ↔ リモート(SSH)両方を想定
  • scp / rsync の基本は scp / rsync の使い分け を参照

rsync の差分同期とは何か?

結論: rsync はコピー先に既にあるファイルを比較し、変化した分だけを転送する。2 回目以降が劇的に速いのがバックアップ・ミラーリングに向く理由。

rsync は単なるコピーツールではなく、転送元と転送先の差分だけを送る 同期ツールである。初回はフルコピーになるが、2 回目以降は「変わったファイルだけ」「変わったブロックだけ」を送るため、大量のデータでも短時間で最新化できる。

この性質が、定期バックアップ(毎日同じディレクトリを最新化)やミラーリング(2 拠点を一致させる)に向いている。

なぜ差分転送が速いのか?

結論: rsync はデフォルトで「サイズ + 更新時刻」が一致するファイルを転送スキップする。差分があるファイルだけ、さらに差分ブロックだけを送るため転送量が小さい。

rsync の高速さは 2 段階の差分判定による。

  1. ファイル単位のスキップ判定: デフォルトでは各ファイルの サイズと更新時刻(mtime) を比較し、両方一致すれば「変更なし」とみなして転送しない
  2. ブロック単位の差分転送: 変更ありと判定したファイルは、ローリングチェックサムで一致するブロックを検出し、変わった部分だけ を送る(rsync アルゴリズム)
# 初回:フルコピー
$ rsync -av src/ /backup/dst/

# 2 回目以降:差分だけ転送(変更がなければ一瞬で終わる)
$ rsync -av src/ /backup/dst/

更新時刻が信用できない環境(クロックずれ・ファイルシステム移行直後など)では、後述の --checksum で中身を直接比較できる。

バックアップの基本形はどう書くのか?

結論: rsync -av src/ dst/ が基本。-a(アーカイブモード)で権限・所有者・タイムスタンプ・シンボリックリンクを保持したまま再帰コピーできる。

実務のバックアップは、まず属性を保持する -a を付けるところから始める。

$ rsync -av /home/user/data/ /backup/data/

-a(アーカイブモード)は次のオプションをまとめたもの。

含まれる内容 意味
-r 再帰(ディレクトリを下位まで)
-l シンボリックリンクをそのまま保持
-p パーミッションを保持
-t タイムスタンプを保持
-g / -o グループ / 所有者を保持
-D デバイス・特殊ファイルを保持

よく併用するオプション。

  • -v:転送内容を表示(-vv でさらに詳細)
  • -z:転送時に圧縮(遅い回線で有効。LAN では無効化した方が速いこともある)
  • -h:サイズを人間可読表示(--progress と相性が良い)

末尾スラッシュの罠(最重要)

rsync -av src/ dst/   # src の「中身」を dst 直下に置く
rsync -av src  dst/   # dst の中に「src ディレクトリごと」置く(dst/src/...)

転送元の末尾 / の有無で結果が変わる。バックアップ先の階層が一段ずれる事故の大半はこれが原因。

ミラーリングはどうするのか?(--delete)

結論: 完全ミラーには --delete を付ける。転送元に存在しないファイルを転送先から削除し、両者を完全一致させる。破壊的なので必ず --dry-run を先に実行する。

-a だけのバックアップは「追加・更新」しかしない。転送元で削除したファイルは、転送先に残り続ける。転送元と転送先を完全に一致させたい(ミラーリング)場合は --delete を使う。

# まず必ず dry-run で「何が削除されるか」を確認
$ rsync -av --delete --dry-run src/ /mirror/dst/

# 表示内容に問題なければ本番実行
$ rsync -av --delete src/ /mirror/dst/

dry-run の出力では、削除されるファイルが deleting ... の行で表示される。

sending incremental file list
deleting old-report.csv
deleting cache/tmp.dat
./
new-report.csv

削除のタイミングを制御するオプションもある。

  • --delete-after:転送が全部終わってから削除(途中失敗時に転送先を壊しにくい)
  • --delete-excluded--exclude で除外したファイルも転送先から削除する

世代バックアップ(増分スナップショット)はどう作るのか?

結論: --link-dest で前回のバックアップを参照すると、変更のないファイルはハードリンクで共有される。各世代が「フルバックアップに見えて、実容量は差分だけ」という効率的なスナップショットになる。

毎日上書きするバックアップは、「3 日前の状態に戻したい」に対応できない。世代を残す には --link-dest を使う。

--link-dest=DIR は、転送先に書き込む際、変更のないファイルを DIR(通常は前回のバックアップ)への ハードリンク として作る。中身が同じファイルはディスクを二重に消費しないため、複数世代を保持しても容量効率が高い。

# 日付ごとのディレクトリにスナップショットを作る例
$ SRC=/home/user/data/
$ DEST=/backup/snapshots
$ TODAY=$(date +%F)          # 例: 2026-06-05
$ LATEST=$DEST/latest        # 前回のスナップショットを指すシンボリックリンク

$ rsync -av --delete \
    --link-dest="$LATEST" \
    "$SRC" "$DEST/$TODAY/"

# latest を最新スナップショットに更新
$ ln -sfn "$DEST/$TODAY" "$LATEST"

これで /backup/snapshots/2026-06-05/ には全ファイルが揃って見えるが、前日から変わっていないファイルは前日分とハードリンクを共有しているため、増えた実容量は差分だけになる。

  • --link-dest のパスは 転送先からの相対 または絶対パスで指定する。相対指定する場合は転送先ディレクトリ基準になる点に注意
  • 世代の削除は、対応する日付ディレクトリを rm -rf するだけでよい。ハードリンクなので、他世代が参照しているファイルの実体は消えない

不要ファイルを除外するには?(--exclude)

結論: --exclude=PATTERN で対象から外す。除外パターンが多い場合は --exclude-from=FILE にまとめる。キャッシュ・ログ・一時ファイルを除くとバックアップが軽くなる。

バックアップにキャッシュや一時ファイルを含めると、容量も転送時間も無駄になる。--exclude で除外する。

$ rsync -av --delete \
    --exclude='*.tmp' \
    --exclude='cache/' \
    --exclude='node_modules/' \
    src/ /backup/dst/

除外が多いときはファイルにまとめる。

# .rsync-exclude の中身(1 行 1 パターン)
# *.tmp
# cache/
# node_modules/
# .git/

$ rsync -av --delete --exclude-from='.rsync-exclude' src/ /backup/dst/

除外パターンの先頭 /転送元のルート基準 を意味する。

  • --exclude='/cache':転送元直下の cache だけ除外
  • --exclude='cache/':どの階層の cache/ も除外

意図しない階層まで除外しないよう、--dry-run で挙動を確認すること。

帯域・中断・進捗をどう制御するのか?

結論: -P--partial --progress)で中断再開と進捗表示、--bwlimit で帯域制限ができる。大容量・低速回線・本番時間帯の転送で必須のオプション。

大きなデータをリモートに送る場合、回線を埋め尽くしたり、途中で切れてやり直しになったりする。次のオプションで制御する。

# 進捗表示 + 中断時に途中ファイルを保持(再開で続きから)
$ rsync -avP src/ user@server:/backup/dst/

# 帯域を 10 MB/s に制限(本番時間帯のバックアップで有効)
$ rsync -av --bwlimit=10M src/ user@server:/backup/dst/

# SSH ポートが標準と違う場合
$ rsync -av -e "ssh -p 2222" src/ user@server:/backup/dst/
オプション 効果
-P --partial(途中ファイル保持)+ --progress(進捗表示)
--partial 中断しても途中まで転送したファイルを残し、次回続行
--bwlimit=RATE 転送帯域の上限(例: 10M = 10 MB/s)
-e "ssh -p PORT" リモートシェルの指定(非標準ポート等)

-z(圧縮)は CPU を使う。LAN や既に圧縮済みのデータ(動画・画像・アーカイブ)では -z を外した方が速いことが多い。低速 WAN で効果が出る。

チェックサム比較はいつ使うのか?

結論: --checksum-c)はサイズ・更新時刻ではなく中身のチェックサムで差分判定する。クロックずれ・FS 移行後の検証など、更新時刻が当てにならない場面で使う。常用は遅いので避ける。

デフォルトの「サイズ + 更新時刻」判定は高速だが、更新時刻が信用できない状況では取りこぼす可能性がある。--checksum は全ファイルのチェックサムを計算して中身で比較するため、確実だが遅い。

# 中身で厳密に差分判定(移行検証・整合性チェック向き)
$ rsync -avc src/ /backup/dst/

使いどころ。

  • ファイルシステム / サーバ移行後に、コピーが完全か検証したい
  • バックアップ元と先の 内容一致を保証 したい(mtime がずれている可能性がある)

--checksum は両側で全ファイルを読み込むため、大量データでは非常に遅い。日常の定期同期はデフォルトの mtime 判定を使い、検証時だけ --checksum を併用するのが定石。

事故を防ぐチェックリスト(まとめ)

結論: --delete を伴う rsync は --dry-run を必須に、末尾スラッシュとパスの空変数を毎回確認する。これだけで rsync の重大事故はほぼ防げる。

次に読む