差分だけ高速・暗号化で安全。サーバーとのファイル同期は rsync × SSH が最適解です。この記事は “いますぐ動く” コマンドと、事故を防ぐ運用テンプレを中心にまとめました。
まずは最短レシピ(コピペ可)
1) 鍵認証の用意
# まだ鍵が無い場合のみ
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 22 user@server.example.com
2) まずはドライラン(実行せず差分確認)
SRC="/path/to/local_dir"
DST="user@server.example.com:/srv/app/shared"
rsync -a -z -P --delete --itemize-changes \
-e 'ssh -p 22 -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=accept-new' \
"$SRC/" "$DST" --dry-run
3) 本番実行
rsync -a -z -P --delete --itemize-changes \
-e 'ssh -p 22 -i ~/.ssh/id_ed25519' \
"$SRC/" "$DST"
✅ ポイント
"$SRC/"の末尾スラッシュは「中身を同期」の意味(超重要)。--deleteは宛先から不要ファイルを削除=完全ミラー。必ずドライランで確認してから。
使い回せる基本構文
- ローカル → リモート(ミラー)
rsync -a -z -P --delete -e 'ssh -p 22' SRC_DIR/ user@host:/path/to/DST_DIR
- リモート → ローカル(取得)
rsync -a -z -P --delete -e 'ssh -p 22' user@host:/path/to/SRC_DIR/ ./DST_DIR
- 除外パターンの指定
# .rsync-exclude を用意(例)
cat > .rsync-exclude <<'EOF'
*.tmp
node_modules/
.DS_Store
cache/
EOF
rsync -a -z -P --delete --exclude-from=.rsync-exclude \
-e 'ssh -p 22' SRC_DIR/ user@host:/path/to/DST_DIR
よく使うオプション早見表
| オプション | 役割 / 効果 | 例 |
|---|---|---|
-a | アーカイブ(再帰・時刻・権限 等) | 基本は常に付ける |
-z | 転送圧縮 | 低速回線で有効 |
-P | 進捗+再開に強い(--partial --progress) | 大きいファイル向け |
--delete | 宛先から不要を削除(完全ミラー) | 要ドライラン確認 |
--itemize-changes | 変更内容を記号で一覧化 | 監査・検証向け |
--exclude(-from) | 除外 | キャッシュ除外など |
--numeric-ids | UID/GID を数字で扱う | 異環境間で整合 |
--bwlimit=5m | 帯域制限 | 業務時間の負荷抑制 |
--info=stats2,progress2 | 詳細統計と進捗 | rsync 3.x 以降 |
macOS の標準 rsync は古いことが多いです。
brew install rsyncで 3.x を導入すると快適。
SSH設定をスッキリ書く(~/.ssh/config)
Host prod
HostName server.example.com
User user
Port 22
IdentityFile ~/.ssh/id_ed25519
StrictHostKeyChecking accept-new
ServerAliveInterval 60
以後はこれでOK:
rsync -a -z -P --delete SRC_DIR/ prod:/srv/app/shared
バスティオン経由(踏み台)
Host bastion
HostName bastion.example.com
User ops
Host prod
HostName private.example.internal
User deploy
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519
# ProxyJump は ssh 側の設定なので、そのまま使える
rsync -a -z -P --delete ./build/ prod:/var/www/app/
日次スナップショット(履歴も残す/リモート)
未変更はハードリンク再利用で容量節約(rsync 先の同一FS上に前回のスナップショットが必要)。
SRC="/data"
HOST="prod"
ROOT="/srv/backup/snapshots"
STAMP="$(date +%F)" # 例: 2025-08-23
TARGET="${ROOT}/${STAMP}"
PREV="${ROOT}/latest" # リモートにある直近スナップショット
# 転送(--link-dest は「宛先サーバー基準の絶対パス/相対パス」で指定)
rsync -a --delete \
--link-dest="$PREV" \
-e ssh "$SRC/" "${HOST}:${TARGET}"
# latest を新しいスナップショットへ付け替え
ssh "$HOST" ln -sfn "$TARGET" "$PREV"
--link-destは「宛先側のパス」を指定します。相対なら 宛先ディレクトリからの相対。
自動化テンプレ(cron+排他+ログ)
# /usr/local/sbin/rsync_push.sh
#!/usr/bin/env bash
set -euo pipefail
SRC="/var/www/app"
DST="prod:/srv/app/shared"
EXC="/etc/rsync/exclude.txt"
LOG="/var/log/rsync_push.log"
flock -n /tmp/rsync_push.lock -c "
rsync -a -z -P --delete --exclude-from=\"$EXC\" \
\"$SRC/\" \"$DST\" >>\"$LOG\" 2>&1
"
crontab -e
# 毎日 02:20 に実行(メール通知不要なら MAILTO= 空)
MAILTO=""
20 2 * * * bash /usr/local/sbin/rsync_push.sh
速度・安定性チューニング
- ローカル高速化:
-W/--whole-file(差分計算を省略、同一ホスト間で有効) - 途中再開:
-P --partial-dir=.rsync-partial - 削除の安全性:
--delete-delay(削除は最後にまとめて) - 大規模同期:
--exclude-fromで一時ファイルやビルド生成物を省く - ネットワーク負荷:
--bwlimit=5m、業務時間外の cron 実行
安全運用チェックリスト
- ✅ 本番前に
--dry-run --itemize-changes - ✅ 末尾スラッシュの意味を固定運用(常に
SRC/) - ✅ 初回は
--deleteを付けず挙動を確認 - ✅ 除外リストは ファイル管理(リポジトリで共有)
- ✅ 復元テスト(たまに逆向き rsync で戻せるか検証)
- ✅
.sshは権限厳密に:chmod 700 ~/.ssh; chmod 600 ~/.ssh/id_ed25519
双方向の自動マージが必要なら rsync ではなく Unison / Syncthing などのツールを検討。
トラブル対処
- 「消えた!」 →
--deleteの対象だった可能性。必ずドライラン&バックアップを。 - 権限が崩れる →
--numeric-ids、ACL/拡張属性は-A -X、宛先FSの対応を確認。 - SSH が落ちる →
ServerAliveInterval、ClientAliveIntervalを設定。 - macOS でオプションが使えない → Homebrew の rsync 3.x を利用。
- 大容量で遅い → 除外の見直し、
-W、帯域制限、夜間実行。
まとめ
- rsync over SSH は「安全・高速・省コスト」の三拍子。
- 運用の肝は ドライラン→本番、末尾スラッシュ、除外とログ、定期同期。
- 履歴が必要なら
--link-destスナップショットで“軽い世代管理”。
このページのレシピをコピペして、今日から 事故らないサーバー同期 を始めましょう。

コメント