MAATRIX / Блог / Два экземпляра Next.js на VPS: общий кеш и обновление без случайных ошибок

Два экземпляра Next.js на VPS: общий кеш и обновление без случайных ошибок

MAATRIX · 17 октября 2026 · 8 мин чтения

Один посетитель видит новую цену, другой — старую. После обновления вкладки они меняются ролями. Разработчик проверяет базу: там всё правильно. Балансировщик тоже исправен — он аккуратно распределяет запросы между двумя экземплярами приложения, каждый из которых уверен в собственной версии мира.

Так выглядит один из самых обыденных сюрпризов масштабирования Next.js. Запустить второй процесс несложно. Сложнее договориться, какую сборку обслуживают процессы, где лежит кеш, как распространяется его сброс и что делать с браузером, открытым до нового выпуска.

Разберём схему для самостоятельного размещения. Она подходит и для двух процессов на одном VPS, и как отправная точка для нескольких серверов. Однако два процесса на одном узле не защищают от отказа самого узла: это разные уровни задачи.

VPS для самостоятельного размещения Next.js

Подберите ресурсы для приложения, кеша и базы. Начните с проверяемого стенда, измерьте нагрузку и подготовьте переключение между выпусками.

Выбрать VPS для Next.js

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

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

В небольшой установке удобно хранить каждый выпуск в отдельном каталоге с неизменяемым именем. В контейнерной — использовать один образ с фиксированным digest. В журнале развёртывания запишите версию Node.js, lockfile и идентификатор выпуска. Тогда фраза «оба сервера обновлены» становится проверяемым утверждением.

Два процесса одной уже собранной версии для локального испытания можно запустить в отдельных сессиях:

npx next start --hostname 127.0.0.1 --port 3001
npx next start --hostname 127.0.0.1 --port 3002

Это команды из каталога подготовленного проекта с установленными зависимостями и выполненным production build. Для постоянной работы используйте выбранный менеджер процессов или контейнеры, а входной HTTPS-трафик принимайте через reverse proxy. Общие особенности такого запуска разобраны в руководстве Next.js по самостоятельному размещению.

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

Кеш — это несколько разных механизмов

Словом «кеш» часто называют всё сразу: страницы, результаты запросов, оптимизированные изображения, данные компонентов, браузер и CDN. Но одинаковый адрес Redis ещё не означает, что все эти уровни согласованы.

Сначала выпишите, что именно кеширует приложение. Для каталога товаров это может быть публичное описание. Для личного кабинета — данные конкретного пользователя, которые нельзя случайно превратить в общий ответ. Для изображений — производные файлы с собственным сроком жизни.

В актуальном Next.js есть важное различие. Параметр cacheHandler относится к серверному кешу соответствующих механизмов, включая ISR. Для директив use cache предназначен отдельный интерфейс cacheHandlers. Имена похожи, но это не взаимозаменяемые настройки.

Выберите обработчик, совместимый с точной версией Next.js и используемым режимом кеширования. Не копируйте небольшой учебный класс с локальным Map как решение для двух серверов: каждый процесс снова получит собственное независимое хранилище.

Для серверного обработчика есть официальный пример с Redis. Его стоит изучать вместе с документацией своей версии, особенно формат сериализации и работу тегов. Ветка примера может двигаться быстрее установленного у вас выпуска.

Сохранить общий ответ мало: нужно сообщить, что он устарел

Представим изменение цены в административной панели. Запрос попал в экземпляр А и вызвал сброс соответствующего кеша. Следующий покупатель попал в экземпляр Б. Если Б не узнал об изменении, общий замысел масштабирования закончился на половине дороги.

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

Нельзя считать любое запаздывание ошибкой. Иногда продукт сознательно допускает устаревшее описание товара в течение некоторого времени. Но цена в оформляемом заказе должна проверяться по правилам магазина на надёжном источнике данных. Кеш интерфейса не должен становиться окончательным арбитром денежных условий.

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

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

Server Actions и секреты выпуска

Если приложение использует Server Functions, экземплярам нужен согласованный ключ шифрования их замыканий. Переменная NEXT_SERVER_ACTIONS_ENCRYPTION_KEY задаётся на этапе сборки; документация описывает допустимый формат ключа. Не путайте её с паролем базы и секретом пользовательской сессии. Назначение механизма объясняется в руководстве по безопасности данных Next.js.

Ключ храните как секрет системы сборки. Не добавляйте префикс NEXT_PUBLIC_, не вставляйте его в репозиторий и не выводите в журнал. Один общий артефакт выпуска помогает сохранить согласованность, но не отменяет контролируемого хранения секретов и процедуры их замены.

Управление сессиями зависит от используемой библиотеки авторизации. Если сессия хранится только в памяти процесса, второй экземпляр может не знать вошедшего пользователя. Выберите поддерживаемое общее хранилище либо проверяемый механизм токенов и одинаковую конфигурацию экземпляров.

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

Что происходит с открытой вкладкой во время выпуска

Пользователь мог открыть форму до обновления сервера. В браузере остаётся старый JavaScript, а сервер уже обслуживает новую сборку. Возможны ошибки загрузки файлов, несовпадение Server Actions и проблемы клиентской навигации.

Настройка deploymentId позволяет обозначать выпуск и обнаруживать расхождение версий. Для всех экземпляров одного выпуска идентификатор одинаков; для следующего он меняется. Например, в next.config.mjs:

const nextConfig = {
  deploymentId: process.env.DEPLOYMENT_VERSION,
};

export default nextConfig;

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

Важно: параметр ?dpl= сам по себе не заставляет ваш балансировщик направлять запрос в нужный выпуск. Если требуется маршрутизация по версии, её настраивают отдельно. Сохранение доступности статических файлов предыдущей сборки на переходный период также входит в план развёртывания.

Не удаляйте старый каталог сразу после запуска нового процесса. Сначала убедитесь, что завершены обслуживаемые запросы и политика доступа к старым ресурсам соответствует выбранной схеме. Экономия нескольких минут хранения обычно не стоит ошибок у людей с давно открытой вкладкой.

Выпуск, который можно остановить

Для небольшой команды понятна схема с отдельным новым выпуском: запустить его на другом порту, проверить доступ к зависимостям и выполнить прикладной тест, затем переключить входящий трафик. Старый процесс завершить корректно после окончания текущих запросов.

Миграции базы должны допускать этот переход. Если новая версия сразу удаляет поле, нужное старой, возможность переключить прокси назад существует только на рисунке. Изменения схемы лучше раскладывать на совместимые этапы: сначала добавить поддержку, затем перевести код и лишь позже убрать старое.

Проверка готовности должна отражать возможность обслужить реальный запрос, а не только наличие процесса. Но не превращайте каждый health check в тяжёлую операцию на базе. Для разных уровней наблюдения нужны разные проверки: процесс жив, зависимости доступны, пользовательский сценарий проходит.

Отдельно проверьте приватность кеширования: войдите двумя тестовыми пользователями и убедитесь, что ответы личного кабинета не пересекаются. Прокси и CDN должны сохранять смысл заголовков и вариантов ответа. Ошибочно закешированная персональная страница — гораздо серьёзнее устаревшей подписи у товара.

Когда имеет смысл второй экземпляр

Для пилота с двумя процессами Next.js можно рассмотреть MAATRIX Heka Standard с 4 vCPU и 8 ГБ памяти. Если на том же сервере работает база и общий кеш, ресурсы считайте суммарно. Сборка приложения тоже потребляет память; её лучше отделить от обслуживания пользователей, если она мешает рабочей нагрузке.

Maat Pro с 8 vCPU и 16 ГБ может дать больше пространства для испытаний, но второй процесс не исправит медленный SQL или слишком тяжёлый рендер. Сначала сравните задержку и потребление ресурсов на одинаковом сценарии. Для устойчивости к потере узла понадобится несколько серверов и продуманное размещение общих зависимостей.

В результате у вас должна получиться воспроизводимая система: один известный выпуск, согласованное состояние, проверяемый сброс кеша и понятный переход между версиями. Тогда масштабирование перестаёт напоминать лотерею, в которой пользователь обновляет страницу до выпадения правильной цены.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →