
Атомарная публикация статического сайта в Nginx
Храним каждую версию сайта в отдельном каталоге, переключаем Nginx одной символической ссылкой и быстро возвращаем предыдущий выпуск.
Содержание
При обычном копировании поверх работающего сайта файлы заменяются по одному. Новый index.html уже может ссылаться на новый JavaScript, который ещё не успел загрузиться, а старое изображение может быть удалено раньше новой страницы. В этот момент посетитель видит смесь двух версий.
Атомарная публикация устраняет промежуточное состояние. Слово «атомарная» здесь означает, что видимое переключение происходит одной неделимой операцией: до неё Nginx читает целую старую версию, после неё — целую новую. Сами файлы заранее загружаются в отдельный непубличный каталог.
Эта схема предназначена для статического сайта без базы данных и пользовательских загрузок внутри каталога версии. Для приложения с изменяемыми данными одного переключения файлов недостаточно.
Из каких каталогов состоит схема
Каждая готовая сборка хранится в своём каталоге внутри releases. Имя может содержать дату и время, чтобы версии не путались. Nginx читает путь current, но это не настоящий каталог, а символическая ссылка на выбранный выпуск.
Символическая ссылка — небольшой объект файловой системы, который перенаправляет обращение на другой путь. Она похожа на ярлык, но программы работают через неё как через обычный каталог.
После двух публикаций структура может выглядеть так:
/var/www/example.org/
├── releases/
│ ├── 2026-09-05-1100/
│ └── 2026-09-05-1430/
└── current -> releases/2026-09-05-1430
Числа в имени — не внутренний технический ID, который нужно расшифровывать. Это просто дата и время подготовки версии: 5 сентября 2026 года, 14:30. Можно добавить короткий номер commit Git, но для работы ссылки он не требуется.
Опубликованные каталоги не редактируют. Если исправление нужно срочно, собирают новый выпуск. Благодаря этому возврат действительно приводит к ранее проверенному состоянию.
Один раз перенесите текущий сайт в releases
До изменения проверьте, что Nginx сейчас читает /var/www/example.org/html, а внутри находится работающий сайт. Выполните ls -la /var/www/example.org и сохраните текущий файл конфигурации Nginx.
Следующие команды создают хранилище выпусков, перемещают существующий каталог под понятным именем и создают ссылку current:
sudo install -d -o admin -g admin -m 755 /var/www/example.org/releases
sudo mv /var/www/example.org/html /var/www/example.org/releases/first-manual
sudo ln -s releases/first-manual /var/www/example.org/current
Первая команда создаёт releases с владельцем admin; замените имя пользователя своим. Вторая не удаляет прежний сайт, а переносит его в releases/first-manual. Третья создаёт относительную ссылку: из каталога example.org она ведёт в releases/first-manual.
Не запускайте блок, если пути releases, first-manual или current уже существуют. Сначала выясните их содержимое и назначение. Команды рассчитаны на переход от структуры предыдущего урока, а не на произвольный рабочий сервер.
Один раз направьте Nginx на current
Откройте конфигурацию домена, которая была создана при подключении Nginx. Директива root сообщает веб-серверу, где искать файлы запрошенного сайта. Замените только её значение, чтобы постоянной точкой входа стала ссылка current:
root /var/www/example.org/current;
Остальные строки блока server остаются прежними. Проверьте конфигурацию и примените это единственное изменение:
sudo nginx -t
sudo systemctl reload nginx
curl -I -H 'Host: example.org' http://127.0.0.1/
nginx -t должен подтвердить успешную проверку. reload нужен сейчас, потому что изменился файл конфигурации. Локальный запрос должен вернуть тот же сайт, который работал до переноса: ссылка current пока ведёт на прежний каталог.
При следующих публикациях строка root не меняется. Nginx продолжает обращаться к current, а процесс публикации заменяет только ссылку.
Подготовьте новый выпуск отдельно от current
Соберите и проверьте сайт на своём компьютере так же, как при первой ручной публикации. Выберите уникальное имя каталога, например 2026-09-05-1430, и передайте результат в домашний каталог пользователя:
scp -r dist admin@203.0.113.10:site-2026-09-05-1430
Команда выполняется локально. Она целиком передаёт каталог dist через SSH; IP, пользователь и имя результата должны быть заменены. Не используйте имя, которое уже существует на VPS.
После загрузки войдите на сервер, проверьте наличие index.html, общий размер и отсутствие закрытых файлов. Затем перенесите готовый каталог в releases:
sudo mv /home/admin/site-2026-09-05-1430 /var/www/example.org/releases/2026-09-05-1430
sudo chown -R admin:admin /var/www/example.org/releases/2026-09-05-1430
sudo find /var/www/example.org/releases/2026-09-05-1430 -type d -exec chmod 755 {} +
sudo find /var/www/example.org/releases/2026-09-05-1430 -type f -exec chmod 644 {} +
Первая команда перемещает уже полностью загруженный каталог. Вторая назначает владельца публикации. Два вызова find отдельно задают права каталогам и обычным файлам: Nginx получает чтение и проход, но не запись.
Путь повторяется намеренно и должен указывать на один новый выпуск. Перед рекурсивными командами проверьте его через ls -ld /var/www/example.org/releases/2026-09-05-1430. Не подставляйте непроверенную пустую переменную.
Проверьте выпуск до переключения посетителей
Сначала убедитесь, что Nginx может прочитать главную страницу нового каталога:
sudo -u www-data test -r /var/www/example.org/releases/2026-09-05-1430/index.html && echo "Файл доступен Nginx"
Тест выполняется с правами рабочего пользователя Nginx. Сообщение появляется только при успехе. Проверьте также наличие важных вложенных страниц, CSS и изображений. Если сборка содержит файл с номером версии, сравните его с ожидаемым выпуском.
Пока current указывает на first-manual, ошибки в новом каталоге не видны посетителям. Исправьте сборку и создайте новый каталог, а не меняйте уже названный готовым выпуск.
Переключите current одной операцией
Нельзя удалять current и затем создавать новую ссылку: между командами путь кратковременно исчезнет. Сначала создайте соседнюю временную ссылку, затем замените ею текущую:
sudo ln -s releases/2026-09-05-1430 /var/www/example.org/current-next
sudo mv -Tf /var/www/example.org/current-next /var/www/example.org/current
Первая команда подготавливает current-next, пока посетители продолжают пользоваться current. Вторая команда переименовывает ссылку поверх существующей. Параметр -T заставляет mv рассматривать current как один объект, а -f разрешает заменить прежнюю ссылку.
Операция атомарна, когда обе ссылки находятся в одной файловой системе, как в этой структуре. Новые открытия файлов после переключения идут в каталог 2026-09-05-1430. Перечитывать конфигурацию Nginx не нужно: её текст не менялся.
Сразу проверьте опубликованную версию
Сначала убедитесь, куда ведёт ссылка, затем проверьте сайт изнутри VPS и с внешнего компьютера:
readlink -f /var/www/example.org/current
curl -I -H 'Host: example.org' http://127.0.0.1/
curl -I https://example.org/
readlink -f должен вывести полный путь нового выпуска. Локальный запрос проверяет выбор сайта в Nginx, внешний — DNS, сетевые фильтры, HTTPS и публичный ответ.
Одного кода 200 мало, если старая и новая главная отвечают одинаково. Откройте характерную новую страницу или проверьте уникальный фрагмент версии. Отдельно запросите несуществующий адрес и убедитесь, что он действительно возвращает 404.
Верните предыдущий выпуск при ошибке
Если новая версия не прошла проверку, снова подготовьте временную ссылку, но направьте её на first-manual:
sudo ln -s releases/first-manual /var/www/example.org/current-rollback
sudo mv -Tf /var/www/example.org/current-rollback /var/www/example.org/current
Это тот же механизм переключения, только целью служит прежний каталог. Сразу повторите локальный и внешний запросы. Не удаляйте неудачный выпуск до разбора: его файлы помогают воспроизвести ошибку.
Такой возврат меняет только статические файлы. Он не отменяет отдельные изменения DNS, конфигурации Nginx или данных приложения. Поэтому эти изменения не следует незаметно включать в одну процедуру публикации.
Когда автоматизировать команды
Ручная схема считается понятой, когда вы можете назвать текущий выпуск, проверить новый каталог, переключить ссылку и вернуть предыдущую версию. После этого повторяющиеся действия можно поместить в скрипт или CI.
Автоматизация должна остановиться до переключения при любой ошибке сборки, передачи или проверки. Она также должна создавать уникальное имя, запрещать параллельные публикации одного сайта и записывать, какая версия стала текущей. Пользователю автоматизации не нужны неограниченные права root.
Не начинайте автоматическую очистку со сложного rm -rf. Сначала просматривайте список каталогов через ls -lt /var/www/example.org/releases и удаляйте конкретный старый выпуск только после успешной новой публикации. Храните минимум одну проверенную версию для возврата и учитывайте свободное место.
Каталоги releases находятся на том же диске и не являются резервной копией. Следующий эксплуатационный этап — контроль доступности и ручная выгрузка копии на локальный компьютер.
Первоисточники
Самопроверка
Проверьте, что материал усвоен
Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.
Разбираем коротко
Частые вопросы
Что означает атомарная публикация?
Новая версия сначала полностью готовится в отдельном каталоге, после чего одна операция меняет ссылку current. Посетители получают либо целую старую, либо целую новую версию, а не смесь файлов во время копирования.
Нужно ли перечитывать конфигурацию Nginx при каждом выпуске?
Нет. Если директива root постоянно указывает на ссылку current, а меняется только цель этой ссылки, конфигурация Nginx остаётся прежней. Reload нужен только после изменения самой конфигурации.
Каталоги старых выпусков заменяют резервную копию?
Нет. Они помогают вернуть неудачную версию сайта, но находятся на том же VPS. Ошибка диска, удаление сервера или потеря учётной записи затронет все выпуски сразу.


