rsync over SSHとは?SSH経由のファイル同期・鍵認証・自動バックアップを解説

実務レシピ

rsync over SSH とは、ファイル同期ツール rsync の通信経路に SSH を使う方法です。差分だけを転送するため高速で、SSH の暗号化により安全にサーバー間のファイル同期・バックアップができます。この記事では、基本コマンドから SSH ポート指定・鍵認証・除外設定・cron 自動化まで、コピペして使える形でまとめています。

  1. rsync over SSH とは
  2. 基本のSSH経由同期コマンド
    1. ローカル → リモートへ同期
    2. リモート → ローカルへ取得
    3. 末尾スラッシュ(/)の違い
  3. SSHポートを指定する
  4. SSH鍵ファイルを指定する
  5. –dry-runで事前確認する
  6. –deleteで完全ミラー同期する
  7. –excludeで除外する
  8. まずは最短レシピ(コピペ可)
    1. 1) 鍵認証の用意
    2. 2) まずはドライラン(実行せず差分確認)
    3. 3) 本番実行
  9. 主要オプション早見表
  10. SSH設定をスッキリ書く(~/.ssh/config)
    1. バスティオン経由(踏み台)
  11. SSH鍵認証を使った自動化(cron)
    1. SSH鍵認証のセットアップ
    2. 自動化スクリプト(cron+排他ロック+ログ)
  12. バックアップ・実用例
    1. 日次スナップショット(履歴も残す)
    2. WordPressファイルのサーバー間同期
    3. サーバー移行(旧サーバー → 新サーバー)
  13. 速度・安定性チューニング
  14. よくある失敗例
    1. 末尾スラッシュの有無で同期先の構造が変わる
    2. --deleteで意図せず削除してしまう
    3. SSHポート指定を忘れてタイムアウトする
    4. 権限・所有者が変わる
    5. 鍵認証が通らずcronで失敗する
  15. 安全運用チェックリスト
  16. よくある疑問(FAQ)
    1. rsyncでSSHを使うにはどうするか
    2. SSHポートを指定するにはどうするか
    3. SSH鍵を指定するにはどうするか
    4. 同期前に確認するにはどうするか
    5. cronで自動同期するにはどうするか
  17. トラブル対処
  18. まとめ
  19. 関連記事

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-idsUID/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 が落ちるServerAliveIntervalClientAliveInterval を設定。
  • 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 スナップショットで"軽い世代管理"。

このページのレシピをコピペして、今日から 事故らないサーバー同期 を始めましょう。

Bash玄

はじめまして!Bash玄です。

エンジニアとしてシステム運用に携わる中で、手作業の多さに限界を感じ、Bashスクリプトを活用して業務を効率化したのがきっかけで、この道に入りました。「手作業は負け」「スクリプトはシンプルに」をモットーに、誰でも実践できるBashスクリプトの書き方を発信しています。

このサイトでは、Bashの基礎から実践的なスクリプト作成まで、初心者でもわかりやすく解説しています。少しでも「Bashって便利だな」と思ってもらえたら嬉しいです!

# 好きなこと
- シンプルなコードを書くこと
- コマンドラインを快適にカスタマイズすること
- 自動化で時間を生み出すこと

# このサイトを読んでほしい人
- Bashに興味があるけど、何から始めればいいかわからない人
- 定型業務を自動化したい人
- 効率よくターミナルを使いこなしたい人

Bashの世界に一歩踏み出して、一緒に「Bash道」を極めていきましょう!

Bash玄をフォローする

コメント