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 段階の差分判定による。
- ファイル単位のスキップ判定: デフォルトでは各ファイルの サイズと更新時刻(mtime) を比較し、両方一致すれば「変更なし」とみなして転送しない
- ブロック単位の差分転送: 変更ありと判定したファイルは、ローリングチェックサムで一致するブロックを検出し、変わった部分だけ を送る(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 は転送先を破壊する
転送元のパスを間違える(例: 空ディレクトリを指定する)と、転送先の 全ファイルが削除される。特に次の組み合わせは事故が起きやすい。
- 転送元の末尾スラッシュの付け忘れ・付けすぎ
src/のsrc部分が変数で、その変数が空のまま展開された
--delete を含む rsync は、本番前に --dry-run を例外なく実行すること。
削除のタイミングを制御するオプションもある。
--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 の重大事故はほぼ防げる。
実行前チェック(特に --delete 使用時)
- [ ] 転送元 / 転送先のパスは正しいか(空変数で展開されていないか)
- [ ] 末尾スラッシュは意図通りか(
src/とsrcは別物) - [ ]
--dry-runで削除・転送対象を確認したか - [ ] 転送先の空き容量は足りるか(
df -hで確認) :::
コピペ用:安全テンプレ
# 1. 片方向バックアップ(追加・更新のみ)
rsync -av src/ /backup/dst/
# 2. 完全ミラー(必ず dry-run を先に)
rsync -av --delete --dry-run src/ /mirror/dst/
rsync -av --delete src/ /mirror/dst/
# 3. 世代スナップショット(前回分をハードリンク)
rsync -av --delete --link-dest=/backup/snapshots/latest \
src/ /backup/snapshots/$(date +%F)/
# 4. リモートへ(進捗 + 帯域制限 + 非標準ポート)
rsync -avP --bwlimit=10M -e "ssh -p 2222" src/ user@server:/backup/dst/