rsync over SSH とは、ファイル同期ツール rsync の通信経路に SSH を使う方法です。差分だけを転送するため高速で、SSH の暗号化により安全にサーバー間のファイル同期・バックアップができます。この記事では、基本コマンドから SSH ポート指定・鍵認証・除外設定・cron 自動化まで、コピペして使える形でまとめています。
rsync over SSH とは
rsync は差分転送に特化したファイル同期ツールです。通常のコピーと違い、変更があったファイルだけを転送するため、大量ファイルの同期でも高速に動作します。
SSH 経由で使うと、転送データが暗号化されるため、インターネット越しのサーバー同期でも安全です。-e ssh オプションで SSH をトンネルとして使います。
- 差分転送:変更分だけ送るので高速・省帯域
- SSH 暗号化:データが暗号化されて安全に転送される
- 冪等性:何度実行しても同じ結果になる
- 柔軟なオプション:除外・削除・バックアップ・帯域制限など多彩な設定が可能
基本のSSH経由同期コマンド
rsync で SSH を使うには -e ssh オプションを指定します。
ローカル → リモートへ同期
rsync -avz -e ssh ./local_dir/ user@example.com:/remote/path/
リモート → ローカルへ取得
rsync -avz -e ssh user@example.com:/remote/path/ ./local_dir/
方向を逆にするだけで「プル」(リモートからローカルへの取得)になります。バックアップや本番サーバーからのデータ取得に使えます。
末尾スラッシュ(/)の違い
rsync では末尾スラッシュの有無で同期の挙動が変わります。間違えると意図しない構造でファイルが置かれるため、必ず理解しておきましょう。
| コマンド | 結果 |
|---|---|
rsync -av src/ dest/ | src の中身を dest に同期(dest/file.txt になる) |
rsync -av src dest/ | src ディレクトリごとを dest に同期(dest/src/file.txt になる) |
# SRC/ の末尾スラッシュあり → 中身だけを同期
rsync -avz -e ssh ./myapp/ user@example.com:/var/www/myapp/
# SRC の末尾スラッシュなし → ディレクトリごと同期(/var/www/myapp/myapp/ になる)
rsync -avz -e ssh ./myapp user@example.com:/var/www/myapp/
⚠️ 末尾スラッシュを間違えると、コピー先にネストしたディレクトリが作られます。SRC/ と書く(末尾スラッシュを付ける)習慣にすると「中身を同期」で統一でき、事故を防げます。
SSHポートを指定する
SSH がデフォルトの 22 番ポート以外を使っている場合は、-e オプション内で指定します。
# SSHポートが 2222 の場合
rsync -avz -e 'ssh -p 2222' ./local_dir/ user@example.com:/remote/path/
VPS やクラウドサーバーでセキュリティのためにSSHポートを変更している場合によく使います。
SSH鍵ファイルを指定する
複数の SSH 鍵を使い分けている場合や、デフォルト以外の鍵を指定したい場合は -i で鍵ファイルを指定します。
# SSH鍵ファイルを明示的に指定する
rsync -avz -e 'ssh -i ~/.ssh/id_ed25519' ./local_dir/ user@example.com:/remote/path/
# ポート指定と鍵指定を組み合わせる
rsync -avz -e 'ssh -p 2222 -i ~/.ssh/id_ed25519' ./local_dir/ user@example.com:/remote/path/
cron での自動化では、-i で鍵を明示することで SSH エージェントなしでも認証が通ります。
–dry-runで事前確認する
--dry-run(または -n)を付けると、実際の転送や削除を行わずに、何が変更されるかだけを確認できます。本番実行前に必ず実行する習慣をつけましょう。
# ドライランで何が同期されるか確認
rsync -avz --dry-run -e ssh ./local_dir/ user@example.com:/remote/path/
# --itemize-changes で変更内容を記号で確認
rsync -avz --dry-run --itemize-changes -e ssh ./local_dir/ user@example.com:/remote/path/
出力例:>f...... は「ファイルを送る」、.d...... は「ディレクトリ情報を更新」などを意味します。
–deleteで完全ミラー同期する
--delete を付けると、転送元に存在しないファイルを転送先からも削除します。完全なミラーリングが実現できますが、誤って削除すると取り戻せないため注意が必要です。
# --deleteを使う場合は必ず--dry-runで先に確認する
rsync -avz --delete --dry-run -e ssh ./local_dir/ user@example.com:/remote/path/
# 確認できたら本番実行
rsync -avz --delete -e ssh ./local_dir/ user@example.com:/remote/path/
⚠️
--deleteは慎重に使いましょう。転送元から削除したファイルが転送先からも消えます。初回実行時は--deleteなしで動作確認してから使うことをおすすめします。
–excludeで除外する
同期から除外したいディレクトリやファイルパターンを指定できます。
# 単一パターンを除外
rsync -avz --exclude 'node_modules/' -e ssh ./local_dir/ user@example.com:/remote/path/
# 複数パターンを除外
rsync -avz \
--exclude 'node_modules/' \
--exclude '.git/' \
--exclude '*.log' \
--exclude '.env' \
-e ssh ./local_dir/ user@example.com:/remote/path/
# 除外リストをファイルで管理する(おすすめ)
cat > .rsync-exclude <<'EOF'
node_modules/
.git/
*.log
.env
cache/
.DS_Store
EOF
rsync -avz --exclude-from=.rsync-exclude -e ssh ./local_dir/ user@example.com:/remote/path/
まずは最短レシピ(コピペ可)
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は宛先から不要ファイルを削除=完全ミラー。必ずドライランで確認してから。
主要オプション早見表
| オプション | 役割 / 効果 | 例 |
|---|---|---|
-a | アーカイブ(再帰・時刻・権限 等) | 基本は常に付ける |
-v | 詳細表示(転送ファイルを一覧化) | 実行内容を確認したいとき |
-z | 転送圧縮 | 低速回線で有効 |
-P | 進捗+再開に強い(--partial --progress) | 大きいファイル向け |
--delete | 宛先から不要を削除(完全ミラー) | 要ドライラン確認 |
--dry-run / -n | 実行せず確認のみ | 本番前の確認に必須 |
--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)
~/.ssh/config にホスト情報をまとめておくと、rsync コマンドがシンプルになります。
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/
SSH鍵認証を使った自動化(cron)
cron から rsync を自動実行するには、SSH 鍵認証が必須です。パスワード認証では cron 実行時に止まってしまいます。
SSH鍵認証のセットアップ
# パスフレーズなしの鍵を生成(自動化では必須)
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519_cron
# 公開鍵をサーバーに登録
ssh-copy-id -i ~/.ssh/id_ed25519_cron.pub user@example.com
# 接続テスト(パスワードなしで繋がることを確認)
ssh -i ~/.ssh/id_ed25519_cron user@example.com echo "OK"
自動化スクリプト(cron+排他ロック+ログ)
# /usr/local/sbin/rsync_push.sh
#!/usr/bin/env bash
set -euo pipefail
SRC="/var/www/app"
DST="user@example.com:/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\" \
-e 'ssh -i ~/.ssh/id_ed25519_cron' \
\"$SRC/\" \"$DST\" >>"$LOG" 2>&1
"
crontab -e
# 毎日 02:20 に実行(メール通知不要なら MAILTO= 空)
MAILTO=""
20 2 * * * bash /usr/local/sbin/rsync_push.sh
cron での実行は SSH エージェントが使えないため、
-iで鍵ファイルを明示し、パスフレーズなしの鍵を使います。鍵認証が通らないと cron 実行時にサイレントで失敗します。
バックアップ・実用例
日次スナップショット(履歴も残す)
未変更はハードリンク再利用で容量節約(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 -i ~/.ssh/id_ed25519' "$SRC/" "${HOST}:${TARGET}"
# latest を新しいスナップショットへ付け替え
ssh "$HOST" ln -sfn "$TARGET" "$PREV"
WordPressファイルのサーバー間同期
WordPress のアップロードファイルやテーマ・プラグインをサーバー間でコピーする場合の例です。
# 本番サーバーから wp-content をローカルに取得
rsync -avz \
--exclude 'cache/' \
--exclude '*.log' \
-e 'ssh -p 22 -i ~/.ssh/id_ed25519' \
user@example.com:/var/www/html/wp-content/ \
./wp-content/
# ローカルの変更を本番に反映(--dry-runで先に確認)
rsync -avz --dry-run \
--exclude 'cache/' \
--exclude 'debug.log' \
-e 'ssh -p 22 -i ~/.ssh/id_ed25519' \
./wp-content/ \
user@example.com:/var/www/html/wp-content/
サーバー移行(旧サーバー → 新サーバー)
サーバー移行時にファイルをまるごと転送する場合、新サーバーから旧サーバーにアクセスして rsync するのが一般的です。
# 新サーバー上で実行:旧サーバーからファイルを引っ張る
rsync -avz \
--exclude 'cache/' \
--exclude '*.tmp' \
-e 'ssh -i ~/.ssh/id_ed25519' \
old-user@old-server.example.com:/var/www/html/ \
/var/www/html/
# 差分チェック(移行後に旧サーバーの変更分だけ再同期)
rsync -avz --dry-run \
-e 'ssh -i ~/.ssh/id_ed25519' \
old-user@old-server.example.com:/var/www/html/ \
/var/www/html/
速度・安定性チューニング
- ローカル高速化:
-W/--whole-file(差分計算を省略、同一ホスト間で有効) - 途中再開:
-P --partial-dir=.rsync-partial - 削除の安全性:
--delete-delay(削除は最後にまとめて) - 大規模同期:
--exclude-fromで一時ファイルやビルド生成物を省く - ネットワーク負荷:
--bwlimit=5m、業務時間外の cron 実行
よくある失敗例
末尾スラッシュの有無で同期先の構造が変わる
# ❌ スラッシュなし:dest/src/file.txt になってしまう
rsync -avz -e ssh ./src user@example.com:/dest/
# ✅ スラッシュあり:dest/file.txt になる(意図通り)
rsync -avz -e ssh ./src/ user@example.com:/dest/
--deleteで意図せず削除してしまう
転送元に存在しないファイルが転送先から消えます。本番ファイルを誤削除したケースが多い。
# 必ず--dry-runで先に確認してから
rsync -avz --delete --dry-run -e ssh ./src/ user@example.com:/dest/
SSHポート指定を忘れてタイムアウトする
# ❌ ポートがデフォルト22でないのに指定なし → 接続失敗
rsync -avz -e ssh ./src/ user@example.com:/dest/
# ✅ -e オプション内にポートを指定
rsync -avz -e 'ssh -p 2222' ./src/ user@example.com:/dest/
権限・所有者が変わる
-a はパーミッションと所有者を保持しますが、転送先のユーザーが異なる場合に所有者がズレます。
# 所有者をリモート側に合わせる場合は --no-owner --no-group
rsync -avz --no-owner --no-group -e ssh ./src/ user@example.com:/dest/
# UID/GIDを数字で扱う場合
rsync -avz --numeric-ids -e ssh ./src/ user@example.com:/dest/
鍵認証が通らずcronで失敗する
cron は SSH エージェントを参照できないため、鍵ファイルを明示しないと認証失敗します。
# ❌ 鍵指定なし → cron実行時に認証失敗(パスワード待ちで止まる)
rsync -avz -e ssh ./src/ user@example.com:/dest/
# ✅ -i で鍵を明示する
rsync -avz -e 'ssh -i /home/user/.ssh/id_ed25519' ./src/ user@example.com:/dest/
安全運用チェックリスト
- ✅ 本番前に
--dry-run --itemize-changes - ✅ 末尾スラッシュの意味を固定運用(常に
SRC/) - ✅ 初回は
--deleteを付けず挙動を確認 - ✅ 除外リストは ファイル管理(リポジトリで共有)
- ✅ 復元テスト(たまに逆向き rsync で戻せるか検証)
- ✅
.sshは権限厳密に:chmod 700 ~/.ssh; chmod 600 ~/.ssh/id_ed25519
双方向の自動マージが必要なら rsync ではなく Unison / Syncthing などのツールを検討。
よくある疑問(FAQ)
rsyncでSSHを使うにはどうするか
-e ssh オプションを付けるだけです。多くの環境では rsync のデフォルトが SSH になっているため、リモートホストを指定すると自動的に SSH が使われます。明示的に書く場合は -e ssh または -e 'ssh -p 22' と指定します。
SSHポートを指定するにはどうするか
-e 'ssh -p 2222' のように、-e オプション内で SSH コマンドのオプションとして指定します。--port ではなく -e 内に書くことに注意してください。
SSH鍵を指定するにはどうするか
-e 'ssh -i ~/.ssh/id_ed25519' と指定します。cron や自動化スクリプトでは絶対パスで指定することをおすすめします。
同期前に確認するにはどうするか
--dry-run(または -n)を付けると、実際には転送・削除せずに何が変更されるかだけ表示できます。--itemize-changes と組み合わせるとより詳細に確認できます。
cronで自動同期するにはどうするか
パスフレーズなしの SSH 鍵を作成し、-e 'ssh -i /path/to/key' で鍵を明示したスクリプトを crontab に登録します。SSH エージェントが使えない cron 環境では鍵の明示が必須です。
トラブル対処
- 「消えた!」 →
--deleteの対象だった可能性。必ずドライラン&バックアップを。 - 権限が崩れる →
--numeric-ids、ACL/拡張属性は-A -X、宛先FSの対応を確認。 - SSH が落ちる →
ServerAliveInterval、ClientAliveIntervalを設定。 - macOS でオプションが使えない → Homebrew の rsync 3.x を利用。
- 大容量で遅い → 除外の見直し、
-W、帯域制限、夜間実行。
まとめ
- rsync over SSH は
-e sshを付けるだけで使える「安全・高速・省コスト」の三拍子。 - SSHポート指定は
-e 'ssh -p 2222'、鍵指定は-e 'ssh -i ~/.ssh/id_ed25519'で対応。 - 運用の肝は ドライラン→本番、末尾スラッシュ、除外とログ、定期同期。
- cron 自動化では SSH 鍵認証(パスフレーズなし)と
-iの明示が必須。 - 履歴が必要なら
--link-destスナップショットで"軽い世代管理"。
このページのレシピをコピペして、今日から 事故らないサーバー同期 を始めましょう。

コメント