scp, sftp и rsync: копирование файлов между Linux-серверами

Если на сервере нет команды scp, не ищите отдельный пакет — его нет. scp, sftp и ssh входят в клиент OpenSSH. В Debian и Ubuntu это пакет openssh-client, в AlmaLinux и Rocky Linux — openssh-clients. На минимальной установке или в контейнере его часто нет, отсюда и ошибка scp: command not found.

Вторая новость: протокол SCP устарел. Начиная с OpenSSH 9.0 команда scp по умолчанию работает поверх SFTP, а старый протокол включается ключом -O. Для пользователя синтаксис почти не изменился, но на старых или урезанных серверах это иногда ломает копирование.

Короткая рекомендация: один-два файла — scp; интерактивная работа с файлами на удалённой машине — sftp; каталоги, большие объёмы и всё, что может оборваться, — rsync -avz --partial. Все три работают через SSH и используют одни и те же ключи и порт.

Установка: если scp, sftp или rsync нет

СистемаКлиент (scp, sftp, ssh)Сервер SFTPrsync
Debian 12 / 13, Ubuntu 24.04apt install openssh-clientopenssh-server + openssh-sftp-serverapt install rsync
AlmaLinux / Rocky 9 и 10dnf install openssh-clientsopenssh-server (sftp-server внутри)dnf install rsync
FreeBSD 14.xOpenSSH в базовой системев базовой системеpkg install rsync

Версии OpenSSH на момент написания: Debian 12 — 9.2, Debian 13 — 10.0, Ubuntu 24.04 — 9.6, AlmaLinux 9 и 10 — 9.9. Свою версию смотрите командой ssh -V.

Есть важная деталь, на которой спотыкались ещё на CentOS: для старого протокола SCP нужен scp на обеих машинах. Удалённый сервер запускает у себя scp -t или scp -f. Если там минимальная система без клиента OpenSSH, вы получите на своей машине ошибку вроде bash: scp: command not found, хотя у вас scp есть. В режиме SFTP на удалённой стороне нужен не scp, а подсистема sftp-server.

rsync тоже должен быть установлен с двух сторон. Иначе будет rsync: command not found и rsync error: remote command not found (code 127).

scp: копирование одной командой

Базовый синтаксис

# файл на удалённый сервер
scp backup.tar.gz user@server.example.ru:/srv/backup/

# файл с сервера к себе
scp user@server.example.ru:/var/log/nginx/error.log ./

# каталог целиком
scp -r /var/www/site user@server.example.ru:/var/www/

# сохранить время изменения и права
scp -p config.yml user@server.example.ru:~/

Основные ключи:

  • -P 2222 — порт SSH. Заглавная P, в отличие от ssh -p.
  • -r — рекурсивно, для каталогов.
  • -p — сохранить время изменения, время доступа и права.
  • -i ~/.ssh/id_ed25519 — конкретный ключ.
  • -C — сжатие. Полезно для текста на медленном канале, бесполезно для архивов и видео.
  • -l 8192 — ограничение скорости в килобитах в секунду (8192 — примерно 1 МБ/с).
  • -J jump@bastion.example.ru — через промежуточный хост.
  • -3 — копирование между двумя удалёнными серверами через вашу машину.
  • -O — использовать старый протокол SCP вместо SFTP.

Почему SCP заменили на SFTP

Старый протокол SCP — это по сути удалённый запуск scp через shell. Имена файлов разбирает shell на удалённой стороне. Отсюда вечные проблемы с пробелами и спецсимволами, двойное экранирование и уязвимости. Например, в 2019 году нашли, что злонамеренный сервер мог подсунуть клиенту лишние файлы (CVE-2019-6111). Разработчики OpenSSH прямо называют протокол устаревшим и негибким.

В OpenSSH 9.0 scp переключили на SFTP (см. примечания к выпуску OpenSSH 9.0). Команды остались прежними, но изменилось поведение:

  • имена с пробелами больше не нужно экранировать дважды;
  • шаблоны * в удалённом пути разбирает сам SFTP, а не shell;
  • ~ в удалённом пути работает, если сервер поддерживает расширение expand-path@openssh.com (OpenSSH 8.9 и новее);
  • на удалённой стороне должна быть включена подсистема SFTP.

Когда нужен -O

Старый протокол через -O нужен в трёх случаях:

  • на удалённом сервере SFTP отключён или не установлен (часто — встроенные системы, роутеры, старые NAS);
  • сервер — не OpenSSH, а, например, Dropbear без SFTP;
  • скрипт полагается на разбор удалённого пути shell-ом, например scp server:'/var/log/*.{1,2}' ..
scp -O firmware.bin root@192.168.1.1:/tmp/

Если новый scp падает с ошибкой вроде scp: Connection closed или subsystem request failed on channel 0, а scp -O работает, — на сервере нет SFTP. Лучше включить его, чем навсегда переходить на -O (см. ниже).

sftp: интерактивная работа с файлами

sftp — это «FTP поверх SSH». Удобен, когда нужно посмотреть, что лежит на сервере, и забрать часть файлов. Отдельный FTP-сервер для этого не нужен.

sftp -P 2222 user@server.example.ru
sftp> ls -l /var/www
sftp> cd /var/www/site
sftp> get -r uploads
sftp> lcd /tmp
sftp> put dump.sql.gz
sftp> reget big.iso
sftp> bye

Полезные команды: get / put (с -r для каталогов), reget / reput — докачка оборванного файла, lcd и lls — работа с локальным каталогом, df -h — место на удалённой стороне.

Для скриптов есть пакетный режим. Команды берутся из файла, при ошибке выполнение останавливается:

cat > batch.txt <<'EOF'
cd /srv/upload
put report.csv
EOF
sftp -b batch.txt user@server.example.ru

Для пакетного режима нужна авторизация по ключу: пароль из файла sftp не прочитает.

Как включить SFTP на сервере

В /etc/ssh/sshd_config должна быть строка Subsystem sftp. Пути отличаются:

# Debian / Ubuntu
Subsystem sftp /usr/lib/openssh/sftp-server

# AlmaLinux / Rocky
Subsystem sftp /usr/libexec/openssh/sftp-server

# или встроенный в sshd вариант, на любой системе
Subsystem sftp internal-sftp

В Debian и Ubuntu сам sftp-server вынесен в пакет openssh-sftp-server. Обычно он ставится вместе с openssh-server как рекомендованный, но при установке с --no-install-recommends его может не быть. После правки проверьте конфигурацию и перечитайте её:

sshd -t && systemctl reload ssh     # Debian / Ubuntu
sshd -t && systemctl reload sshd    # AlmaLinux / Rocky

Не закрывайте текущую сессию SSH, пока не проверите вход в новом окне.

rsync: каталоги, большие объёмы, докачка

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

Базовая команда

rsync -avz --partial /var/www/site/ user@server.example.ru:/var/www/site/
  • -a — архивный режим: рекурсивно, с правами, временем, симлинками, владельцем и группой (владелец сохраняется, если на приёмнике вы root);
  • -v — показывать файлы;
  • -z — сжатие при передаче;
  • --partial — не удалять недокачанный файл при обрыве, чтобы продолжить с того же места;
  • -P — сокращение для --partial --progress.

Слэш в конце пути

Самая частая ошибка с rsync. /var/www/site/ со слэшем значит «содержимое каталога». /var/www/site без слэша — «сам каталог». Команда rsync -a site user@host:/var/www/site/ создаст на приёмнике /var/www/site/site.

Нестандартный порт и ключ

rsync -avz -P -e "ssh -p 2222 -i ~/.ssh/id_ed25519" ./backup/ user@server.example.ru:/srv/backup/

Зеркало с удалением лишнего

# сначала пробный запуск: покажет, что будет удалено
rsync -avn --delete /var/www/site/ user@server.example.ru:/var/www/site/

# затем реальный
rsync -av --delete /var/www/site/ user@server.example.ru:/var/www/site/

--delete удаляет на приёмнике файлы, которых нет в источнике. Перепутанные местами источник и приёмник с этим ключом — классический способ потерять данные. Всегда запускайте сначала с -n (--dry-run).

Другие полезные ключи

  • --exclude='cache/', --exclude-from=файл — исключения;
  • --bwlimit=5000 — ограничение скорости, по умолчанию в КиБ/с (здесь около 5 МБ/с);
  • --info=progress2 — общий прогресс по всей передаче вместо прогресса по каждому файлу;
  • -H, -A, -X — жёсткие ссылки, ACL и расширенные атрибуты (нужны для полного переноса системы);
  • --append-verify — дописать оборванный большой файл и проверить контрольную сумму целиком.

Пример полного переноса сайта на WordPress с базой данных — в статье о переносе WordPress из командной строки.

Ключи SSH: чтобы не вводить пароль

Для регулярного копирования и скриптов нужна авторизация по ключу. Пароли в скриптах и утилиты вроде sshpass — плохая идея: пароль оказывается в истории команд, в списке процессов и в файлах.

# создать ключ (ed25519 — короткий и быстрый)
ssh-keygen -t ed25519 -C "backup@web1"

# скопировать публичный ключ на сервер
ssh-copy-id -p 2222 user@server.example.ru

# проверить
ssh -p 2222 user@server.example.ru 'echo ok'

Чтобы не писать порт и пользователя каждый раз, добавьте хост в ~/.ssh/config. Эти настройки подхватят ssh, scp, sftp и rsync:

Host web1
    HostName server.example.ru
    Port 2222
    User deploy
    IdentityFile ~/.ssh/id_ed25519
scp dump.sql.gz web1:/tmp/
rsync -avP ./site/ web1:/var/www/site/
sftp web1

Для ключа резервного копирования на сервере можно ограничить действия. Для rsync в пакете есть скрипт rrsync: в authorized_keys перед ключом пишется command="rrsync /srv/backup",restrict, и по этому ключу можно только обмениваться файлами с указанным каталогом. Путь к скрипту и имя пакета отличаются по дистрибутивам — проверьте man rrsync.

Скорость: что реально помогает

Копирование по SSH упирается в одно из трёх: канал, диск или процессор (шифрование). Сначала поймите, что именно.

  • Много мелких файлов. Задержка на каждом файле съедает всё время. Упакуйте поток в tar:
    tar -C /var/www -czf - site | ssh web1 'tar -C /var/www -xzf -'

  • Быстрая локальная сеть. Сжатие (-z, -C) замедляет: процессор не успевает. Уберите его. Для rsync в сети 1–10 Гбит/с при первом копировании помогает -W (передавать файлы целиком, без поиска различий).
  • Уже сжатые данные (архивы, видео, образы qcow2 со сжатием). -z не даёт выигрыша. В rsync 3.2 и новее можно указать --compress-choice=zstd, но для таких данных проще отключить сжатие.
  • Слабый процессор. На процессорах с AES-NI быстрее всего AES-GCM: scp -c aes128-gcm@openssh.com. На ARM без аппаратного AES обычно быстрее chacha20-poly1305@openssh.com — шифр по умолчанию.
  • Медленный канал и общий сервер. Ограничьте скорость, чтобы не положить сайт: scp -l или rsync --bwlimit.

Старые советы вроде «используйте шифр arcfour» не работают: слабые шифры давно удалены из OpenSSH, и ssh просто откажется подключаться.

Для больших образов виртуальных машин сначала уменьшите сам файл (сжатие, удаление пустых блоков) — об этом в статье про образы дисков QEMU-KVM, Xen и VMDK. Разреженные файлы rsync копирует с ключом -S (--sparse), иначе на приёмнике они займут полный объём.

Готовые примеры

Дамп базы сразу на другой сервер, без временного файла

mysqldump --single-transaction site_db | gzip | ssh web2 'cat > /srv/backup/site_db.sql.gz'

Место на исходном сервере не расходуется. Пароль к базе держите в ~/.my.cnf с правами 600, а не в командной строке.

Забрать логи за прошлые дни с сервера

rsync -av --include='*.gz' --exclude='*' web1:/var/log/nginx/ ./logs/web1/

Порядок правил важен: сначала включение нужных файлов, затем исключение всех остальных.

Большой файл по нестабильному каналу

rsync -av --partial --append-verify --timeout=60 web1:/srv/images/vm.qcow2 /backup/

Если связь оборвалась, повторите ту же команду: rsync допишет файл и проверит его целиком. Для надёжности её можно завернуть в цикл until rsync ...; do sleep 30; done.

Ежедневная копия каталога по расписанию

# /etc/cron.d/site-backup
15 3 * * *  backup  rsync -a --delete /var/www/site/ backup-host:/srv/backup/site/ >> /var/log/site-backup.log 2>&1

Перед тем как ставить такую задачу в cron, один раз выполните её вручную с -n и проверьте вывод. Учтите: --delete делает зеркало, а не историю версий. Если файл удалили на сайте, к утру его не будет и в копии.

Сравнение: что выбрать

Задачаscpsftprsync
Скопировать один-два файлаЛучший выборМожноМожно
Посмотреть и выбрать файлы на сервереНетЛучший выборНет
Большой каталог, повторная синхронизацияКопирует всё зановоНеудобноЛучший выбор
Докачка после обрываНетreget, reput--partial, --append-verify
Нужно на удалённой сторонеSFTP-сервер (или scp для -O)SFTP-серверrsync
Работает с роутерами и встроенными системамиЧасто только с -OЕсли есть SFTPРедко

Симптомы и решения

СимптомПричинаРешение
scp: command not found на своей машинеНе установлен клиент OpenSSHapt install openssh-client или dnf install openssh-clients
bash: scp: command not found при scp -OНа удалённом сервере нет клиента OpenSSHУстановить его на сервере или работать без -O
subsystem request failed, Connection closedНа сервере отключён SFTPВключить Subsystem sftp или временно scp -O
rsync: command not found, код 127rsync не установлен на удалённой сторонеУстановить rsync на сервере
ssh: connect to host ... port 22: Connection refusedSSH на другом портуscp -P, sftp -P, rsync -e "ssh -p ..." или ~/.ssh/config
Permission denied (publickey)Ключ не добавлен или неверные права на ~/.sshssh-copy-id; права 700 на ~/.ssh, 600 на authorized_keys
Мусор в выводе, «protocol version mismatch» у rsync или sftpСкрипт входа (.bashrc) что-то печатает при неинтерактивном входеУбрать вывод из .bashrc или обернуть проверкой на интерактивность
Каталог оказался внутри одноимённого каталогаНет слэша в конце пути источника rsyncПисать src/, если нужно содержимое

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

scp удалят из OpenSSH?

Команда scp остаётся, меняется только протокол под ней. Старый протокол SCP пока доступен через -O, но разработчики считают его устаревшим. В новых скриптах для каталогов лучше сразу использовать rsync или sftp.

Можно ли копировать через scp с паролем в скрипте?

Технически да, через sshpass, но так делать не стоит. Пароль будет виден в процессах и истории. Создайте отдельный ключ для скрипта, ограничьте его в authorized_keys и храните на сервере с правами 600.

Чем scp -r отличается от rsync -a?

scp -r каждый раз копирует всё заново и не сохраняет владельца и симлинки так, как rsync. rsync -a передаёт только изменения, сохраняет атрибуты и умеет продолжать после обрыва. Для повторных синхронизаций rsync выигрывает во всём.

Как скопировать файл с одного удалённого сервера на другой?

Самый простой вариант — scp -3 web1:/path/file web2:/path/: данные идут через вашу машину. Если серверы видят друг друга по сети, быстрее зайти на первый и запустить rsync оттуда. Для этого используйте проброс агента ssh -A только на доверенных серверах.

Как дать пользователю доступ только к файлам, без shell?

В sshd_config настройте блок Match User или Match Group с ForceCommand internal-sftp и ChrootDirectory. Каталог chroot должен принадлежать root и не быть доступным на запись другим. Такой пользователь сможет работать через sftp и новый scp, но не получит shell.