Руководства

Команда rsync: примеры, ключи, exclude, --delete и SSH

rsync -av SOURCE/ DEST/ копирует папку, а потом передаёт только изменения. Примеры для SSH, исключений, --delete и пробного запуска, разбор ключей и ошибок.

Опубликовано ~10 мин чтения

Коротко: rsync -av SOURCE/ DESTINATION/ копирует каталог, а каждый следующий запуск передаёт только изменившиеся файлы. Удалённая сторона записывается как USER@HOST:/path, и обмен с ней идёт по SSH, поэтому одна и та же команда выкладывает сайт на сервер, забирает бэкап или синхронизирует папку между двумя машинами.

Linux · macOS · WSL
rsync -av ./site/ USER@SERVER_IP:/var/www/site/
rsync -av USER@SERVER_IP:/var/www/site/ ./site-backup/

Первая строка загружает содержимое site на сервер, вторая скачивает его обратно. rsync должен быть установлен на обеих машинах (sudo apt install rsync в Debian и Ubuntu, sudo dnf install rsync в Rocky Linux). Весь вывод ниже получен в rsync 3.2.7 — это версия из Debian 12 и Ubuntu 24.04 — на тестовых каталогах, а удалённая сторона там, где это отмечено, имитирована. Свои сообщения rsync не переводит: даже с русской локалью они выводятся по-английски.

Примеры rsync для типовых задач

ЗадачаКомандаПримечание
Загрузить содержимое папкиrsync -av ./site/ USER@SERVER_IP:/var/www/site/Слеш у источника: копируется содержимое, а не сама папка
Скачать с сервераrsync -av USER@SERVER_IP:/var/log/nginx/ ./logs/Удалённая сторона пишется первой
Другой порт SSH или ключrsync -av -e "ssh -p 2222 -i ~/.ssh/id_ed25519" ./site/ USER@SERVER_IP:/srv/site/Или используйте псевдоним из ~/.ssh/config
Посмотреть, ничего не меняяrsync -avn --delete ./site/ USER@SERVER_IP:/srv/site/-n — это --dry-run
Точное зеркалоrsync -av --delete ./site/ USER@SERVER_IP:/srv/site/Удаляет лишние файлы на приёмнике
Исключить файлыrsync -av --exclude='.git/' --exclude='*.log' ./app/ USER@SERVER_IP:/srv/app/Шаблоны разобраны ниже
Одна строка прогресса на всю задачуrsync -a --info=progress2 /data/ USER@SERVER_IP:/data/rsync 3.1 и новее
Докачать большой файлrsync -aP disk.img USER@SERVER_IP:/srv/-P — это --partial --progress
Переместить вместо копированияrsync -av --remove-source-files ./uploads/ USER@SERVER_IP:/srv/uploads/Удаляет у источника каждый переданный файл; пустые папки остаются
Никогда не перезаписывать файлы приёмникаrsync -av --ignore-existing ./site/ USER@SERVER_IP:/srv/site/-u вместо этого пропускает только файлы, которые на приёмнике новее
Ограничить скоростьrsync -a --bwlimit=5M /data/ USER@SERVER_IP:/data/Без суффикса — КиБ/с
Сжимать при передачеrsync -avz ./dump.sql USER@SERVER_IP:/root/Только текст и только на медленных каналах
Сравнивать по содержимомуrsync -avc ./site/ USER@SERVER_IP:/srv/site/Читает каждый файл одинакового размера на обеих сторонах
Создать недостающие каталоги на приёмникеrsync -av --mkpath ./site/ USER@SERVER_IP:/srv/new/site/rsync 3.2.3 и новее
Сохранить жёсткие ссылки, ACL, xattrsrsync -aHAX /srv/ /mnt/backup/srv/-a не включает ни одно из трёх
Писать в каталоги, принадлежащие rootrsync -av --rsync-path="sudo rsync" ./conf/ USER@SERVER_IP:/etc/myapp/Нужен sudo без пароля для rsync, а это равносильно root

Слеш в конце: содержимое или сама папка

Что окажется на приёмнике, решает слеш у источника. Мы выполнили каждую строку с папкой site, в которой лежит index.html, один раз при отсутствующем приёмнике и один раз при существующем; результат оба раза совпал:

КомандаРезультат
rsync -a site/ backup/backup/index.html: содержимое site
rsync -a site/ backupТо же самое: для папки слеш у приёмника ничего не меняет
rsync -a site backup/backup/site/index.html: сама папка, уровнем глубже
rsync -a site backupbackup/site/index.html, даже если backup до этого не было

Последняя строка — то место, где rsync расходится с cp -r site backup: cp кладёт файлы прямо в новую папку backup. Слеш у приёмника важен только в одном случае: когда один файл копируется по пути, которого ещё нет. rsync -a index.html save/dir создаст файл с именем dir, а save/dir/ создаст папку и положит в неё index.html. Кроме того, слеш сдвигает корень, от которого отсчитываются шаблоны исключений со слешем в начале.

Что значат ключи -a, -v и другие основные

  • -a (archive) — это -rlptgoD: рекурсивно, симлинки как симлинки, с сохранением прав, времени, группы, владельца, а также файлов устройств и специальных файлов. Он не включает жёсткие ссылки (-H), ACL (-A) и расширенные атрибуты (-X); для бэкапа системы добавьте их.
  • Владельцы сохраняются, только если rsync на принимающей стороне работает от root; иначе все файлы будут принадлежать пользователю, под которым вы вошли. Для бэкапов между машинами с разными списками пользователей добавьте --numeric-ids.
  • -v выводит список файлов, -h печатает размеры вида 32.01M, а -i показывает, почему передаётся каждый файл (следующий раздел).
  • rsync пропускает файл, если размер и время изменения на обеих сторонах совпадают. Из-за этой «быстрой проверки» (quick check) второй запуск идёт быстро, и из-за неё же существует -c.

Сначала пробный запуск: -n и -i

-n (--dry-run) проходит всё сравнение и ничего не меняет. Мы добавили в источник contact.html, изменили index.html и удалили about.html, и пробный запуск на прежнюю копию вывел:

Пример вывода · rsync -av --delete --dry-run site/ mirror/
sending incremental file list
deleting about.html
./
contact.html
index.html

sent 300 bytes  received 42 bytes  684.00 bytes/sec
total size is 37  speedup is 0.11 (DRY RUN)

Замените -v на -i, и каждая строка скажет, что именно изменится:

Пример вывода · rsync -ai --delete --dry-run site/ mirror/
*deleting   about.html
.d..t...... ./
>f+++++++++ contact.html
>f.st...... index.html

>f значит, что файл передаётся (<f — когда он уходит на удалённый хост), +++++++++ — что он новый, s и t — что отличаются размер и время, .d — каталог, у которого меняются атрибуты без передачи данных, а *deleting — файл, который удалил бы --delete.

--delete без потери данных

--delete удаляет на приёмнике файлы, которых больше нет в источнике, и именно это делает копию зеркалом. Он затрагивает только каталоги, которые участвуют в передаче: в нашем пробном запуске rsync -a --delete site mirror/ (без слеша) только создал бы mirror/site/ и ничего не удалил. Настоящая опасность — пустой или неверный источник. Опечатка в пути, несмонтированный диск для бэкапов или пустой checkout — и rsync послушно опустошит приёмник:

Пример вывода · пустой источник, rsync -ai --delete --dry-run empty/ mirror/
*deleting   css/style.css
*deleting   css/
*deleting   index.html
*deleting   about.html
.d..t...... ./
  1. 1Запустите ровно эту команду вхолостую

    Добавьте -n (и -i) к команде, которую собираетесь выполнить, больше ничего в ней не меняя.

  2. 2Прочитайте каждую строку *deleting

    Если список длиннее, чем вы ожидали, остановитесь и проверьте оба пути и слеши.

  3. 3Запустите с ограничителем

    Уберите -n. В скриптах добавляйте --max-delete=50: после этого числа rsync пропускает оставшиеся удаления и завершается с кодом 25.

Исключённые файлы защищены от --delete, если не добавить --delete-excluded: так переживают зеркалирование node_modules или .env, которые есть только на сервере. Чтобы удалять только после того, как пришли все файлы, используйте --delete-after.

Как исключить файлы и папки

--exclude=PATTERN можно повторять; * совпадает в пределах одного уровня пути, слеш в конце — только с каталогами, а шаблон без слеша в начале совпадает на любой глубине. Слеш в начале привязывает шаблон к корню передачи, а этот корень зависит от слеша у источника: с источником site/ шаблон --exclude='/node_modules' пропускает только папку верхнего уровня, а с источником site тот же шаблон не совпадает ни с чем, и нужен /site/node_modules. Длинные списки удобнее держать в файле: по одному шаблону на строку, # для комментариев:

excludes.txt
# исключения для деплоя
.git/
node_modules/
*.log
.env
Linux · macOS
rsync -av --exclude-from=excludes.txt ./app/ USER@SERVER_IP:/srv/app/

Чтобы скопировать только некоторые типы файлов, сначала перечислите включения, потому что срабатывает первое подходящее правило: --include='*/' --include='*.php' --exclude='*' копирует все файлы .php в дереве, а --prune-empty-dirs убирает оставшиеся пустыми папки. С --exclude='*' в начале наш тест не скопировал вообще ничего.

rsync по SSH: порт, ключ и sudo

rsync запускает ssh и выполняет на сервере второй rsync, поэтому он и должен быть там установлен. Мы подменили ssh скриптом, который только записывает свои аргументы; для rsync -a -e "ssh -p 2222" ./site/ deploy@203.0.113.10:/var/www/site/ он получил:

Аргументы, которые rsync передал ssh
-p 2222 -l deploy 203.0.113.10 rsync --server -logDtpre.iLsfxCIvu . /var/www/site/
  • Порт, ключ и промежуточный хост проще всего задать в ~/.ssh/config (англ.): с псевдонимом myvps команде rsync -av ./site/ myvps:/var/www/site/ ключ -e вообще не нужен.
  • Для каталога, в который может писать только root, нужен --rsync-path="sudo rsync" и правило sudoers, которое разрешает этому пользователю запускать rsync без пароля, потому что ввести пароль некому. Такое правило — это root-доступ под другим названием: этот аккаунт может перезаписать любой файл, включая /etc/sudoers. Давайте его только отдельному пользователю для деплоя или загружайте файлы в домашний каталог и переносите их через sudo.
  • Пути с пробелами заключайте в кавычки один раз, для своей оболочки. Начиная с 3.2.4 rsync сам экранирует удалённый путь; для "deploy@203.0.113.10:/srv/My Files/" в логе было видно, что уходит /srv/My\ Files/.

Для ключа бэкапа, который должен уметь только rsync, в Debian 12 есть /usr/bin/rrsync (скрипт на Python). Поставьте command="rrsync -wo /srv/backups",restrict и пробел перед строкой этого ключа в ~/.ssh/authorized_keys на сервере, и ключ сможет только писать внутрь этого каталога, причём пути на приёмнике будут отсчитываться от него: ни оболочки, ни других путей.

Прогресс, недокачанные файлы, сжатие и ограничение скорости

-P объединяет --progress (строка на каждый файл) и --partial, который сохраняет наполовину переданный файл, чтобы следующий запуск продолжил, а не начал заново. Если файлов много, --info=progress2 показывает всю задачу одной строкой. Вот она с ограничением 5 МБ/с, чтобы цифры были как на реальном канале:

Пример вывода · rsync -a --info=progress2 --bwlimit=5M big/ dst/
      5,604,480  17%    5.00MB/s    0:00:05
     16,745,600  52%    5.00MB/s    0:00:02
     32,000,000 100%    5.04MB/s    0:00:06 (xfr#2, to-chk=0/3)
  • --bwlimit без суффикса считает в КиБ в секунду, так что --bwlimit=5000 — это примерно 5 МБ/с, почти как --bwlimit=5M.
  • -z сжимает данные файлов. Начиная с 3.2.0 обе стороны выбирают первый метод из списка Compress list клиента (см. rsync --version), который есть и у сервера; для двух сборок Debian 12 это zstd. Сжатие окупается для SQL-дампов и логов на медленном канале и впустую тратит CPU на архивах, изображениях и видео, которые уже сжаты.
  • --partial-dir=.rsync-partial прячет недокачанные файлы в скрытую папку, а не держит их под настоящим именем.

Когда стоит включать -c (--checksum)

Быстрая проверка доверяет размеру и времени изменения. Мы дали файлу на приёмнике тот же размер и ту же метку времени, что у источника, но другое содержимое: обычный запуск его пропустил, а rsync -aic поймал:

Пример вывода · rsync -aic --dry-run site/ ck/
>fc........ t.txt

c отмечает расхождение контрольной суммы. Подвох в том, что rsync до начала передачи читает на обеих сторонах каждый файл с совпадающим размером, поэтому -c нужен для проверки миграции или бэкапа, а не для каждого деплоя.

Бэкапы: --backup-dir и снапшоты через --link-dest

--backup --backup-dir=DIR сначала переносит в DIR каждый файл, который был бы перезаписан или удалён. Относительный DIR отсчитывается от приёмника, поэтому используйте ../, чтобы держать его рядом с копией:

Linux
rsync -a --delete --backup --backup-dir="../changed-$(date +%F)" /srv/site/ /backup/current/

В строке crontab пишите \%F вместо %F: голый % cron считает переводом строки.

Для ежедневных снапшотов, которые выглядят как полные копии, но хранят только изменения, укажите в --link-dest вчерашнюю папку. Неизменённые файлы станут жёсткими ссылками:

Linux
rsync -a --link-dest=../2026-11-06 /srv/site/ /backup/2026-11-07/

В нашем тесте у каждого неизменённого файла было две ссылки, а du -sh 2026-11-06 2026-11-07 насчитал 24K для первого снапшота и 8K для второго: один новый файл и сама папка. du учитывает файл с жёсткими ссылками один раз за запуск, поэтому второй снапшот сам по себе показывает свои полные 28K. Старый снапшот удаляется обычным rm -r; файлы, на которые ещё ссылаются более новые, остаются. Чтобы запускать его каждую ночь, Cron Expression Generator (англ.) соберёт и объяснит строку расписания.

Копирование между двумя серверами

Пример вывода · обе стороны удалённые
The source and destination cannot both be remote.
rsync error: syntax or usage error (code 1) at main.c(1437) [Receiver=3.2.7]

rsync не умеет передавать данные между двумя удалёнными хостами через себя, как это делает scp -3. Войдите на один сервер и запустите rsync там: ssh USER@SERVER_A "rsync -a /srv/data/ USER@SERVER_B:/srv/data/". Для этого на сервере A нужен ключ, который примет сервер B. Можно и пробросить свой агент через ssh -A, но тогда любой, у кого есть root на сервере A, может пользоваться этим агентом, пока вы подключены. Данные при этом идут напрямую между серверами, а не через ваш домашний канал.

Демон rsync (rsync://, порт 873)

Помимо SSH, rsync умеет работать с rsync --daemon на TCP-порту 873, который настраивается в /etc/rsyncd.conf с именованными модулями: rsync -av rsync://HOST/MODULE/ ./ или HOST::MODULE в старой записи. Через него раздают файлы зеркала дистрибутивов Linux. Протокол не шифруется, поэтому свои данные передавайте по SSH или держите демон в частной сети. В rsync 3.4.0 (январь 2025 года) исправлены шесть уязвимостей: часть — в демоне, часть позволяла вредоносному серверу атаковать клиента. Debian 12 перенёс исправления в свой пакет 3.2.7, так что ориентируйтесь на обновления apt, а не на номер версии.

Частые ошибки rsync

rsync: command not found на сервере

Пример вывода
bash: line 1: rsync: command not found
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: remote command not found (code 127) at io.c(232) [sender=3.2.7]

Первая строка пришла от оболочки сервера: rsync там нет. Установите его на сервере или укажите через --rsync-path копию, которая лежит вне PATH. На большинстве серверов эта строка английская. Если на сервере сгенерирована русская локаль, а SSH передал LANG (в Debian это включено по умолчанию: SendEnv LANG LC_* у клиента и AcceptEnv LANG LC_* у сервера), bash напишет bash: строка 1: rsync: команда не найдена. Две строки самого rsync остаются английскими в любом случае.

mkdir failed: No such file or directory (2)

Пример вывода · rsync -a site/ /srv/deep/a/b/
rsync: [Receiver] mkdir "/srv/deep/a/b" failed: No such file or directory (2)
rsync error: error in file IO (code 11) at main.c(800) [Receiver=3.2.7]

rsync создаёт только последний каталог пути на приёмнике. Добавьте --mkpath (3.2.3 и новее) или создайте родительские каталоги командой ssh USER@SERVER_IP mkdir -p /srv/deep/a.

Permission denied (13) и код 23

Строки вроде rsync: [receiver] mkstemp "/var/www/site/.index.html.…" failed: Permission denied (13), за которыми следует some files/attrs were not transferred (see previous errors) (code 23), значат, что пользователь, под которым вы вошли, не может писать в этот путь или читать этот файл. Исправьте владельца на приёмнике или воспользуйтесь вариантом с sudo из раздела про SSH. Код 24 (файлы исчезли во время передачи) нормален для живых каталогов с логами или кешем.

Connection refused, таймауты и Permission denied (publickey)

Это сбои SSH, а не rsync. Сначала идёт строка от ssh, а rsync добавляет к ней две свои:

Пример вывода · сервер отверг ключ
deploy@203.0.113.10: Permission denied (publickey).
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: unexplained error (code 255) at io.c(232) [sender=3.2.7]

Выполните ssh -v USER@SERVER_IP с тем же портом и сначала почините SSH; со стороны ключа поможет статья про Permission denied (publickey). Port Checker (англ.) покажет, отвечает ли порт SSH из интернета вообще.

Нужно место для этих бэкапов? VPS с большим диском от VMHeaven — полноценный KVM-сервер, который выбирают по объёму диска. Он рассчитан на приём данных от rsync, restic или Borg по SSH и стоит отдельно от машины, которую защищает.

Частые вопросы

Что значит слеш в конце пути в rsync?

Слеш у источника копирует содержимое папки: rsync -a site/ backup/ положит index.html прямо в backup/. Без слеша rsync копирует саму папку, и получится backup/site/index.html, даже если backup раньше не было. Слеш у приёмника при копировании папки ничего не меняет.

Как запустить rsync по SSH на нестандартном порту?

Передайте команду ssh через ключ -e: rsync -av -e "ssh -p 2222" ./site/ user@host:/var/www/site/. В кавычки можно добавить -i ~/.ssh/id_ed25519 для конкретного ключа или прописать Port и IdentityFile в ~/.ssh/config и использовать псевдоним вместо user@host.

Есть ли rsync для Windows?

Встроенного нет. Удобнее всего установить WSL и пользоваться rsync из Linux-дистрибутива внутри него; rsync при этом должен быть установлен и на сервере. Для разовой передачи файлов хватит scp, который входит в Windows. На macOS начиная с 15.4 /usr/bin/rsync — это openrsync с урезанным набором ключей, а полноценный rsync ставится через brew install rsync.

Читайте также

Все статьи раздела