NFSv4 — простой и предсказуемый способ дать нескольким Linux-серверам общий каталог: медиафайлы проекта, артефакты сборки, общие uploads, резервную staging-копию или файловое хранилище для внутренних сервисов. На облачных VDS это особенно удобно, когда есть отдельный сервер с диском побольше и несколько приложений, которым нужен доступ к одним и тем же файлам.
В этой инструкции настроим NFSv4 Linux в практичном варианте: один сервер экспортирует каталог, клиенты монтируют его безопасно и без подвисания загрузки. Отдельно разберём exports, сопоставление пользователей через idmapd, права доступа, systemd automount, firewall NFS и диагностику типовых ошибок.
Главная идея NFSv4: в большинстве обычных сценариев достаточно TCP-порта 2049, понятной структуры экспортов и одинаковой модели UID/GID на сервере и клиентах.
Когда NFSv4 подходит, а когда лучше подумать дважды
NFS хорош для доверенной инфраструктуры: несколько VDS в одной приватной сети, backend-сервисы одного проекта, CI-узлы, внутренние админские задачи. Он даёт привычную файловую семантику Linux: каталоги, права, владельцы, блокировки, обычные операции чтения и записи. Для приложений это выглядит как локальная файловая система, хотя данные находятся на другом сервере.
Но NFS не стоит воспринимать как магическую замену распределённому хранилищу. Один NFS-сервер остаётся единой точкой отказа, задержка сети влияет на операции с мелкими файлами, а некорректные права быстро превращаются в «почему PHP не может записать upload». Для высоконагруженных баз данных NFS обычно плохая идея: базы любят локальный диск с предсказуемыми задержками, fsync и контролем кэша.
Хорошие сценарии для NFSv4 на VDS:
- общий каталог медиафайлов для нескольких web-нод;
- хранилище артефактов сборки и деплоя;
- каталоги обмена между внутренними сервисами;
- read-only раздача конфигураций или статических данных;
- централизованное место для бэкапов небольших проектов, если отдельно продуманы снапшоты и копии.
Плохие или рискованные сценарии: хранение активного PostgreSQL/MySQL datadir, общие PHP-сессии без понимания блокировок и garbage collection, публично доступный NFS через интернет без строгого ограничения источников. Если выбираете между сетевыми файловыми системами для небольшой инфраструктуры, полезно отдельно сравнить NFS и SSHFS для серверного хранилища.
Схема стенда и базовые допущения
Дальше будем использовать такую схему:
- NFS-сервер:
10.10.0.10, hostnamenfs1.example.internal; - клиент:
10.10.0.21, hostnameweb1.example.internal; - экспортируемый каталог на сервере:
/srv/nfs/projects; - точка монтирования на клиенте:
/mnt/projects; - NFS-домен для idmap:
example.internal.
IP-адреса и имена замените на свои. Важно, чтобы сервер и клиенты видели друг друга по сети, а firewall разрешал доступ к NFS только нужным адресам. Для production-окружения лучше использовать приватные адреса, а не открывать порт 2049 всему интернету.

Установка пакетов NFS
Пакеты отличаются по семействам дистрибутивов, но логика одинаковая: на сервер ставим NFS server, на клиент — утилиты для монтирования. Если одна и та же VDS будет и сервером, и клиентом, можно поставить оба набора.
Debian и Ubuntu
На сервере:
sudo apt update
sudo apt install nfs-kernel-server nfs-common
sudo systemctl enable --now nfs-serverНа клиенте достаточно:
sudo apt update
sudo apt install nfs-commonAlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux и Fedora
На сервере:
sudo dnf install nfs-utils
sudo systemctl enable --now nfs-serverНа клиенте ставится тот же пакет:
sudo dnf install nfs-utilsПроверить, что серверная часть запущена, можно так:
systemctl status nfs-server
ss -ltnp | grep 2049Для NFSv4 обычно достаточно, чтобы слушался 2049/tcp. Старые сервисы вроде rpcbind, mountd и дополнительных динамических портов важнее для NFSv3. В этой статье мы целимся именно в NFSv4, чтобы упростить firewall и поведение клиентов.
Готовим каталог и пользователей
Самый частый источник проблем — несовпадение владельцев. NFS передаёт не «имя пользователя» как красивую строку, а права, завязанные на UID/GID и механизм idmap. Если на сервере пользователь deploy имеет UID 1001, а на клиенте UID 1001 принадлежит другому пользователю, получится неприятный сюрприз.
Есть два рабочих подхода. Первый — синхронизировать UID/GID для технических пользователей на всех серверах. Второй — использовать NFSv4 idmapping с одинаковым доменом и согласованными именами. На небольших VDS-проектах проще всего комбинировать: завести одинакового пользователя и группу с одинаковыми числовыми идентификаторами.
Создадим группу и пользователя для приложения. Если пользователь уже существует, проверьте его UID/GID командой id.
sudo groupadd --gid 2000 webshared
sudo useradd --uid 2000 --gid 2000 --home-dir /nonexistent --shell /usr/sbin/nologin webshared
id websharedНа RHEL-based системах путь к nologin может быть другим:
sudo useradd --uid 2000 --gid 2000 --home-dir /nonexistent --shell /sbin/nologin websharedПодготовим каталог на NFS-сервере:
sudo mkdir -p /srv/nfs/projects
sudo chown webshared:webshared /srv/nfs/projects
sudo chmod 2770 /srv/nfs/projectsРежим 2770 включает setgid-бит на каталоге: новые файлы и подкаталоги будут наследовать группу webshared. Это удобно, когда несколько процессов на разных клиентах пишут в общий каталог через одну группу.
Настройка /etc/exports для NFSv4
В NFSv4 часто используют псевдокорень экспорта. Это верхняя точка, относительно которой клиенты видят остальные каталоги. Например, экспортируем /srv/nfs как корень с fsid=0, а внутри него — каталог projects.
Откройте /etc/exports на сервере и добавьте правила. В примере доступ разрешён только клиенту 10.10.0.21:
/srv/nfs 10.10.0.21(rw,fsid=0,crossmnt,no_subtree_check,root_squash,sec=sys)
/srv/nfs/projects 10.10.0.21(rw,sync,no_subtree_check,root_squash,sec=sys)Если клиентов несколько, укажите их отдельными записями или используйте подсеть. Например, для приватной сети 10.10.0.0/24:
/srv/nfs 10.10.0.0/24(rw,fsid=0,crossmnt,no_subtree_check,root_squash,sec=sys)
/srv/nfs/projects 10.10.0.0/24(rw,sync,no_subtree_check,root_squash,sec=sys)Разберём важные параметры exports:
rwразрешает чтение и запись. Для статических данных лучше использоватьro.fsid=0задаёт псевдокорень NFSv4.crossmntпозволяет клиенту проходить через смонтированные внутри подкаталоги, если они появятся.syncзаставляет сервер подтверждать запись только после сохранения данных. Это безопаснее, но может быть медленнее.no_subtree_checkотключает проверку поддерева, снижая количество странных ошибок при переименованиях.root_squashпревращает root на клиенте в анонимного пользователя на сервере. Это нормальная защитная настройка.sec=sysозначает обычную UNIX-аутентификацию по UID/GID.
Примените экспорт и проверьте итог:
sudo exportfs -rav
sudo exportfs -vЕсли exportfs ругается на синтаксис, внимательно проверьте пробелы. В /etc/exports между адресом клиента и скобкой с опциями не должно быть пробела: 10.10.0.21(rw,...) — правильно, 10.10.0.21 (rw,...) — уже другой смысл.
Настраиваем idmapd: чтобы владельцы не превращались в nobody
Для NFSv4 сопоставление имён пользователей связано с доменом idmap. Если домен на сервере и клиентах разный, файлы могут отображаться владельцем nobody или 4294967294. Даже если запись работает, администрировать такие каталоги неудобно.
Откройте /etc/idmapd.conf на сервере и клиентах. Найдите секцию [General] и задайте одинаковый домен:
[General]
Domain = example.internalПосле изменения перезапустите службы. На Debian/Ubuntu:
sudo systemctl restart nfs-idmapd
sudo systemctl restart nfs-serverНа RHEL-based и Fedora чаще используется общий набор служб из nfs-utils. Если отдельный unit отсутствует, это нормально: перезапустите NFS-сервер и очистите keyring idmap на клиентах.
sudo systemctl restart nfs-server
sudo nfsidmap -cНа клиенте после правки idmapd.conf выполните:
sudo nfsidmap -c
sudo umount /mnt/projects
sudo mount /mnt/projectsЕсли точка ещё не была смонтирована, команда umount вернёт ошибку — это не страшно. Главное, чтобы после нового mount владельцы отображались ожидаемо:
ls -ln /mnt/projects
ls -l /mnt/projectsКоманда ls -ln показывает числовые UID/GID, а ls -l — имена. Если числа правильные, но имена другие, проблема в локальной базе пользователей. Если числа неожиданные, ищите расхождение UID/GID или idmap.
Firewall NFS: открываем только нужное
Для NFSv4 без NFSv3-клиентов базовое правило простое: разрешить 2049/tcp от доверенных IP-адресов. Не открывайте NFS на весь мир. Даже если у вас строгие exports, firewall должен быть первым рубежом.
UFW на Debian/Ubuntu
sudo ufw allow from 10.10.0.21 to any port 2049 proto tcp
sudo ufw status verboseДля подсети:
sudo ufw allow from 10.10.0.0/24 to any port 2049 proto tcpfirewalld на RHEL-based и Fedora
Если используете firewalld, лучше создать правило с источником. Пример для подсети:
sudo firewall-cmd --permanent --add-rich-rule=rule family=ipv4 source address=10.10.0.0/24 port port=2049 protocol=tcp accept
sudo firewall-cmd --reload
sudo firewall-cmd --list-allЕсли у вас выделенная зона для приватного интерфейса, можно разрешить сервис NFS только в этой зоне. Но не добавляйте nfs в публичную зону без ограничения источников.
nftables
Минимальный пример правила для уже существующей цепочки input:
sudo nft add rule inet filter input ip saddr 10.10.0.0/24 tcp dport 2049 acceptЕсли политика по умолчанию drop, убедитесь, что разрешены established-соединения и SSH, иначе можно случайно отрезать себе доступ. Для аккуратного изменения firewall на удалённой VDS держите открытой вторую SSH-сессию и заранее подготовьте откат.
Ручное монтирование NFSv4 на клиенте
Создадим точку монтирования на клиенте:
sudo mkdir -p /mnt/projectsТак как у нас есть псевдокорень /srv/nfs с fsid=0, клиент может монтировать каталог относительно него:
sudo mount -t nfs4 -o nfsvers=4.2,proto=tcp,hard,timeo=600,retrans=2 10.10.0.10:/projects /mnt/projectsПроверьте тип файловой системы и параметры:
findmnt /mnt/projects
nfsstat -mСоздадим тестовый файл от имени пользователя, который должен писать в общий каталог. Если приложение работает под www-data, nginx, apache или отдельным deploy-пользователем, тестируйте именно им, а не root.
sudo -u webshared touch /mnt/projects/test-from-client
ls -l /mnt/projects/test-from-clientЕсли root может создать файл, а приложение нет, это не «NFS сломался», а обычная проблема прав. Проверьте владельца каталога, группу, setgid-бит, umask приложения и соответствие UID/GID.
Постоянное подключение через /etc/fstab
Самый простой вариант — добавить запись в /etc/fstab. Но для сетевых файловых систем важно не подвесить загрузку сервера, если NFS-сервер временно недоступен. Поэтому используем _netdev, nofail и systemd-automount.
10.10.0.10:/projects /mnt/projects nfs4 rw,nfsvers=4.2,proto=tcp,hard,timeo=600,retrans=2,_netdev,nofail,noauto,x-systemd.automount,x-systemd.idle-timeout=300 0 0Что дают эти параметры:
noautoне монтирует ресурс немедленно во время boot как обычный диск;x-systemd.automountсоздаёт automount-точку: реальное подключение произойдёт при первом обращении;_netdevпомечает ресурс как сетевой;nofailне переводит загрузку в аварийный режим, если ресурс недоступен;x-systemd.idle-timeout=300размонтирует ресурс после 300 секунд простоя;hardзаставляет операции ждать восстановления сервера, а не возвращать приложению ошибку записи слишком рано.
Примените изменения:
sudo systemctl daemon-reload
sudo systemctl restart remote-fs.target
systemctl list-units --type=automount
findmnt /mnt/projectsЕсли findmnt ещё не показывает NFS-монтирование, обратитесь к каталогу:
ls /mnt/projects
findmnt /mnt/projectsТакой подход особенно полезен для web-серверов и воркеров: система загружается быстро, а NFS подключается только тогда, когда действительно нужен. При недоступном NFS-сервере зависнет конкретная операция доступа к каталогу, а не весь boot-процесс.
Отдельные systemd unit-файлы: когда fstab уже тесен
/etc/fstab хорош для большинства задач. Но если нужно явно описывать зависимости, таймауты и порядок запуска, можно создать пару unit-файлов .mount и .automount. Имя mount-unit зависит от пути: /mnt/projects превращается в mnt-projects.mount.
Файл /etc/systemd/system/mnt-projects.mount:
[Unit]
Description=NFSv4 projects mount
After=network-online.target
Wants=network-online.target
[Mount]
What=10.10.0.10:/projects
Where=/mnt/projects
Type=nfs4
Options=rw,nfsvers=4.2,proto=tcp,hard,timeo=600,retrans=2,_netdev
[Install]
WantedBy=multi-user.targetФайл /etc/systemd/system/mnt-projects.automount:
[Unit]
Description=Automount NFSv4 projects
[Automount]
Where=/mnt/projects
TimeoutIdleSec=300
[Install]
WantedBy=multi-user.targetАктивируем automount:
sudo systemctl daemon-reload
sudo systemctl enable --now mnt-projects.automount
systemctl status mnt-projects.automountНе включайте одновременно и fstab-запись, и ручные unit-файлы для одной точки монтирования. Выберите один способ, иначе диагностика станет запутанной: systemd может генерировать mount-unit из fstab и параллельно видеть ваш файл.

Параметры клиента: hard, soft, таймауты и версии
Параметр hard означает, что клиент будет ждать восстановления NFS-сервера. Это хорошо для целостности данных: приложение не получает ложного успеха или случайной ошибки там, где операция записи ещё может завершиться после восстановления связи. Минус — процессы могут зависать в состоянии ожидания I/O.
soft заставляет операции завершаться ошибкой после таймаутов. Это иногда применяют для read-only ресурсов или неважных данных, но для записи пользовательских файлов лучше не использовать: риск повреждения данных и непредсказуемого поведения приложения выше, чем выгода.
Параметры timeo и retrans задают поведение повторов. В примерах выше timeo=600 соответствует 60 секундам, потому что значение указывается в десятых долях секунды. Это консервативная настройка: она не создаёт слишком агрессивных повторов при кратких сетевых паузах.
Версию NFS лучше указывать явно: nfsvers=4.2 или nfsvers=4.1. Если сервер и клиент современные, 4.2 обычно работает нормально. Если видите странные проблемы совместимости, попробуйте 4.1 и проверьте журнал.
Права доступа и root squash на практике
root_squash часто кажется неудобным: root на клиенте не может просто взять и записать файл как root на сервере. Но это защита от ситуации, когда компрометация одного клиента автоматически даёт полный root-доступ к экспортированным данным.
Правильный путь — не отключать root_squash, а настроить владельцев и группы. Например, web-приложение пишет в каталог от имени webshared или через группу webshared. Администраторские операции выполняются на NFS-сервере напрямую или через sudo с понятными правилами.
Проверочный набор команд:
id webshared
namei -l /mnt/projects
getfacl /mnt/projects
sudo -u webshared touch /mnt/projects/write-checkЕсли используете ACL, убедитесь, что они включены и понятны всем администраторам проекта. Простые права UNIX с общей группой обычно легче сопровождать, чем сложная сетка ACL, о которой никто не помнит через полгода.
Диагностика: что смотреть, когда не монтируется
Начинайте с сети. Клиент должен достучаться до сервера по TCP 2049:
nc -vz 10.10.0.10 2049
ss -tn dst 10.10.0.10Если порт закрыт, смотрите firewall на сервере, маршрут между VDS и то, на каком адресе слушает NFS. На сервере полезны:
ss -ltnp | grep 2049
sudo exportfs -v
sudo journalctl -u nfs-server -n 100 --no-pagerНа клиенте:
sudo mount -vvv -t nfs4 10.10.0.10:/projects /mnt/projects
sudo journalctl -b | grep -i nfs
nfsstat -mОшибка access denied by server while mounting обычно означает, что IP клиента не совпал с правилом в /etc/exports, экспорт не применён через exportfs -rav, или клиент монтирует не тот путь. Для NFSv4 с fsid=0 путь 10.10.0.10:/projects может быть правильным, а 10.10.0.10:/srv/nfs/projects — нет, потому что клиент видит псевдокорень, а не реальный путь сервера.
Если владельцы отображаются как nobody, проверьте /etc/idmapd.conf на всех узлах, очистите idmap-кэш и перемонтируйте ресурс. Если запись невозможна, проверьте не только exports, но и обычные права Linux на серверном каталоге.
Диагностика зависаний и stale file handle
Если процессы зависли при обращении к каталогу, сначала проверьте доступность NFS-сервера. При hard-монтировании это ожидаемое поведение: клиент ждёт восстановления. Посмотреть процессы в ожидании I/O можно так:
ps -eo pid,stat,wchan:32,cmd | grep nfs
cat /proc/mounts | grep nfs
journalctl -b | grep -i nfsОшибка Stale file handle появляется, когда объект на сервере был удалён, переименован или экспорт изменился так, что клиент держит устаревшую ссылку. Обычно помогает закрыть процессы, которые держат каталог, и перемонтировать ресурс:
sudo lsof +f -- /mnt/projects
sudo fuser -vm /mnt/projects
sudo umount /mnt/projects
sudo mount /mnt/projectsЕсли обычный umount не проходит из-за занятости, не спешите применять ленивое размонтирование в продакшене. Сначала найдите процессы через lsof и fuser. Ленивое размонтирование может скрыть проблему и оставить приложение в неожиданном состоянии.
Производительность: базовые проверки без преждевременной магии
У NFS есть параметры rsize и wsize, влияющие на размер блоков чтения и записи. Современные клиенты часто подбирают адекватные значения автоматически. Перед ручной настройкой посмотрите, что реально используется:
nfsstat -mДля грубой проверки последовательной записи можно использовать dd, но не делайте далеко идущих выводов только по одному тесту:
dd if=/dev/zero of=/mnt/projects/test.bin bs=1M count=1024 status=progress
sync
rm /mnt/projects/test.binЕсли приложение работает с тысячами мелких файлов, важнее будет задержка сети и количество metadata-операций, а не красивая скорость последовательной записи. В таких случаях помогает кэширование на уровне приложения, упаковка артефактов, уменьшение числа файлов и отказ от NFS для горячего пути запроса.
Следите и за сервером: NFS может упереться в диск, CPU, сетевой интерфейс или лимиты виртуализации. Для первого осмотра достаточно iostat, pidstat, sar, ss и журналов. Если задержки диска растут, тюнинг NFS не спасёт — нужно разбираться с I/O.
Мини-чеклист безопасной production-настройки
- Используйте NFSv4 и открывайте только
2049/tcpдля доверенных IP. - Не публикуйте NFS в интернет без сетевых ограничений.
- Держите
root_squashвключённым, если нет очень веской причины. - Синхронизируйте UID/GID или аккуратно настройте
idmapd. - Для boot-safe подключения используйте
systemd automount,nofailи_netdev. - Для данных на запись предпочитайте
hard, а неsoft. - Проверяйте бэкапы NFS-сервера: общий каталог не отменяет резервное копирование.
- Документируйте, какие клиенты имеют доступ и почему они перечислены в
exports.
Короткий рабочий пример целиком
На сервере: установить пакеты, создать каталог, прописать экспорт, применить настройки и открыть firewall только для клиента.
sudo mkdir -p /srv/nfs/projects
sudo chown webshared:webshared /srv/nfs/projects
sudo chmod 2770 /srv/nfs/projects
sudo editor /etc/exports
sudo exportfs -rav
sudo systemctl restart nfs-server
sudo ufw allow from 10.10.0.21 to any port 2049 proto tcpСодержимое /etc/exports:
/srv/nfs 10.10.0.21(rw,fsid=0,crossmnt,no_subtree_check,root_squash,sec=sys)
/srv/nfs/projects 10.10.0.21(rw,sync,no_subtree_check,root_squash,sec=sys)На клиенте: установить утилиты, создать точку, добавить automount в fstab, проверить доступ.
sudo mkdir -p /mnt/projects
sudo editor /etc/fstab
sudo systemctl daemon-reload
sudo systemctl restart remote-fs.target
ls /mnt/projects
findmnt /mnt/projectsСтрока /etc/fstab:
10.10.0.10:/projects /mnt/projects nfs4 rw,nfsvers=4.2,proto=tcp,hard,timeo=600,retrans=2,_netdev,nofail,noauto,x-systemd.automount,x-systemd.idle-timeout=300 0 0Итоги
NFSv4 на Linux VDS — несложная, но требовательная к аккуратности технология. Надёжная настройка держится на нескольких вещах: понятные exports, одинаковый idmapd-домен, согласованные UID/GID, закрытый firewall NFS и правильное подключение через systemd automount. Если сделать это сразу, общий каталог будет вести себя предсказуемо и не станет причиной долгих простоев при перезагрузке или сетевой паузе.
Для небольших и средних проектов NFSv4 остаётся хорошим инструментом: он прозрачен для приложений, легко диагностируется стандартными Linux-командами и не требует сложного кластера. Главное — не переоценивать его: важные данные всё равно нужно бэкапить, доступ ограничивать, а производительность проверять на реальной нагрузке вашего приложения.


