Фреймворк Next.js и среда выполнения Node.js стали стандартом разработки современных веб-приложений, интерактивных клиентских сервисов и личных кабинетов. Однако этап переноса локально написанного проекта на сервер VDS часто вызывает у вебмастеров и разработчиков сложности: облачные PaaS-платформы вроде Vercel требуют регулярных платежей в валюте, блокируют аккаунты или жестко ограничивают лимиты запросов на бесплатных тарифах.
Запуск приложения напрямую через npm run start или node server.js в окне SSH-терминала — распространенная ошибка начинающих специалистов. При закрытии консоли сессия обрывается, в случае необработанной ошибки Node.js процесс падает без автоматического перезапуска, а при перезагрузке операционной системы сайт остается недоступным. Кроме того, встроенный веб-сервер Node.js не предназначен для прямой отдачи тяжелой статики в интернет без обратного прокси и средств защиты от сетевых атак.
В этом практическом руководстве мы разберем production-стек деплоя: связку отказоустойчивого менеджера процессов PM2, высокопроизводительного обратного прокси-сервера Nginx и автоматического SSL-сертификата Let's Encrypt на сервере с Ubuntu 24.04 LTS.
1. Архитектура продакшен-стека: почему Node.js нужен Nginx и PM2
Для надежной работы веб-приложения на VDS роли между компонентами разделяются следующим образом:
- PM2 (Process Manager): Выполняет роль демонизатора и супервизора Node.js. Запускает приложение в фоновом режиме, перезапускает процесс при сбоях или исчерпании лимита памяти, собирает логи и распределяет нагрузку по ядрам процессора в кластерном режиме (Cluster Mode).
- Nginx (Reverse Proxy): Принимает внешние HTTP/HTTPS запросы на портах 80 и 443, берет на себя тяжелую нагрузку по TLS-терминации, напрямую отдает статические файлы сборки (папки
/_next/static/и/public/) без привлечения движка V8, сжимает трафик через gzip/brotli и проксирует динамические запросы на внутренний сокет или локальный порт127.0.0.1:3000. - Systemd & Certbot: Обеспечивают автоматический подъем менеджера PM2 при перезагрузке виртуального сервера и своевременное продление криптографических сертификатов SSL без участия человека.
Совет инженера по производительности
Отдача статических файлов напрямую через Nginx экономит до 60% оперативной памяти и ресурсов CPU сервера. Движок V8 в Node.js однопоточен, и перекладывание рутины по чтению CSS, JavaScript-чанков и изображений на асинхронный Nginx освобождает воркеры приложения для быстрой генерации динамического SSR-контента.
2. Требования к серверу VDS и сайзинг ресурсов под Next.js
Специфика сборки (build) и работы (runtime) Next.js заключается в том, что процессу компиляции Webpack/Turbopack требуется кратковременно в 2–3 раза больше оперативной памяти, чем работающему приложению в режиме обслуживания посетителей:
| Сценарий проекта | Процессор | Память (RAM) | Накопитель | Режим PM2 |
|---|---|---|---|---|
| Лендинг / Простой сайт | 1 vCPU (3.0+ ГГц) |
1 ГБ RAM + 2 ГБ Swap |
15–20 ГБ NVMe |
fork (1 инстанс) |
| Интернет-магазин / SSR | 2 vCPU |
2–4 ГБ RAM |
30–50 ГБ NVMe |
cluster (-i max) |
| Высоконагруженный сервис / SaaS | 4+ vCPU |
8+ ГБ RAM |
80+ ГБ NVMe |
cluster (4 инстанса) |
Математика памяти в кластерном режиме PM2
Каждый воркер кластера PM2 — это изолированный процесс Node.js со своим собственным движком V8 и выделенной памятью. Если у вас VDS с 2 vCPU и вы запускаете 2 инстанса с лимитом 350 МБ, только на процессы Node.js потребуется до 700 МБ RAM. На серверах с 1 vCPU и 1 ГБ RAM настоятельно рекомендуется задавать instances: 1 (режим fork), а кластерный режим масштабирования подключать на тарифах от 2 ГБ RAM.
Опасность сбоев OOM Killer при выполнении npm run build
Команда сборки Next.js (next build) на этапе генерации страниц и статической оптимизации активно потребляет память. Если ваш VDS имеет 1 ГБ RAM, компилятор неизбежно упадет с ошибкой JavaScript heap out of memory или процесс будет аварийно убит системным демоном Linux OOM Killer. Обязательно создайте Swap-файл минимум на 2 ГБ перед первой сборкой на сервере.
3. Надежные VDS-провайдеры для размещения Node.js и Next.js приложений
Для бесперебойной работы Node.js критически важна высокая однопоточная производительность vCPU (частота от 3.3 ГГц) и скоростные диски NVMe для быстрой компиляции модулей из node_modules.
Рекомендуемые VDS-провайдеры
Отказоустойчивые сервера с быстрыми NVMe и каналом до 1 Гбит/с
Timeweb Cloud — Быстрые NVMe VDS
Высокочастотные процессоры 3.3+ ГГц для мгновенной компиляции Next.js, быстрые NVMe-диски и безлимитный трафик 1 Гбит/с.
Selectel — Надежные облачные серверы
Высокая отказоустойчивость дата-центров Tier III, гарантированные ядра vCPU для SSR-рендеринга и легкое масштабирование ресурсов.
Beget — Удобно для веб-разработчиков
Удобная панель управления сервером, надежная KVM-виртуализация, бесплатные ежедневные бэкапы и мгновенный запуск за 1 минуту.
4. Подготовка окружения на Ubuntu 24.04 LTS: Node.js 22 LTS и Swap
Подключитесь к вашему VDS по SSH. Обновите индекс пакетов и создайте файл подкачки (Swap), если объем оперативной памяти составляет менее 2 ГБ:
# Обновление системных пакетов
sudo apt update && sudo apt upgrade -y
# Создание и активация Swap-файла размером 2 ГБ
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# Добавление Swap в автозагрузку
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Установите актуальную стабильную версию Node.js 22 LTS через официальный репозиторий NodeSource и глобальный пакетный менеджер PM2:
# Установка зависимостей и добавление репозитория NodeSource (Node.js 22.x LTS)
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs build-essential git nginx
# Проверка установленных версий
node -v
npm -v
# Глобальная установка менеджера процессов PM2
sudo npm install -g pm2
5. Подготовка и сборка проекта Next.js: режим Standalone
В современных версиях Next.js (начиная с 13+ и актуальной 15+) встроен механизм сборки Standalone Output. Он анализирует зависимости проекта и создает минималистичный изолированный серверный бандл, которому не требуется вся многогигабайтная папка node_modules в продакшене.
В файле next.config.js (или next.config.mjs) вашего проекта активируйте директиву output: 'standalone':
// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
reactStrictMode: true,
poweredByHeader: false
};
export default nextConfig;
Создайте выделенную директорию для веб-проекта на сервере и установите безопасные права доступа (директории 755, владелец — ваш непривилегированный пользователь):
# Создаем каталог приложения
sudo mkdir -p /var/www/my-next-app
sudo chown -R $USER:$USER /var/www/my-next-app
cd /var/www/my-next-app
# Клонируем репозиторий или копируем исходные файлы
git clone https://github.com/your-username/my-next-app.git .
# Устанавливаем зависимости и выполняем сборку продакшен-бандла
npm ci
npm run build
При использовании output: 'standalone' билд создает специальный файл .next/standalone/server.js. Скопируйте статические файлы в директорию standalone:
# Копируем статику и публичные ассеты для автономного сервера
cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/
6. Конфигурация PM2: кластерный режим, лимиты памяти и автозагрузка
Критическое требование: архитектура Stateless для кластерного режима
Кластерный режим PM2 распределяет входящие запросы между воркерами по алгоритму Round-Robin без привязки сессий к конкретному процессу (session stickiness отсутствует). Это накладывает строгое архитектурное ограничение: приложение обязано быть Stateless.
- Сессии пользователей: Категорически запрещено хранить сессии в переменных оперативной памяти (in-memory). Сессии должны храниться либо в защищенных подписанных JWT/Cookie на стороне клиента, либо в централизованном внешнем хранилище (Redis или базе данных).
- WebSocket и события реального времени: Если приложение использует Socket.IO или Server-Sent Events, воркеры должны обмениваться событиями через брокер сообщений (например, с использованием
@socket.io/redis-adapter). В противном случае сообщение, отправленное клиентом на воркер №1, не будет доставлено клиенту, подключенному к воркеру №2.
Вместо передачи параметров через длинные CLI-команды создайте декларативный файл конфигурации ecosystem.config.js в корневой папке проекта:
// /var/www/my-next-app/ecosystem.config.js
module.exports = {
apps: [
{
name: 'next-app',
script: '.next/standalone/server.js',
instances: 'max', // Автоматическое распределение по всем ядрам vCPU (Cluster Mode)
exec_mode: 'cluster',
autorestart: true,
watch: false,
max_memory_restart: '350M', // Автоперезапуск инстанса при превышении 350 МБ RAM (защита от утечек)
env: {
NODE_ENV: 'production',
PORT: 3000,
HOSTNAME: '127.0.0.1' // Слушаем строго локальный интерфейс (защита от сканеров)
}
}
]
};
Запустите приложение под управлением PM2 и сохраните состояние в системную службу systemd:
# Запуск приложения по файлу конфигурации
pm2 start ecosystem.config.js
# Проверка статуса процессов и потребления ресурсов
pm2 status
# Настройка автозапуска PM2 при ребуте операционной системы
pm2 startup systemd
# Выполните команду, которую выведет терминал (sudo env PATH=$PATH... pm2 startup systemd -u ...)
# Затем зафиксируйте текущий список запущенных процессов
pm2 save
Полезные команды администратора для работы с PM2
Для просмотра логов в реальном времени используйте pm2 logs next-app. Для мониторинга дашборда CPU и памяти в терминале выполните pm2 monit. Для обновления без простоя пользователей используйте команду мягкой перезагрузки pm2 reload next-app.
7. Настройка Nginx Reverse Proxy: кэширование статики, WebSocket и сжатие
Создайте конфигурационный файл виртуального хоста Nginx /etc/nginx/sites-available/next-app.conf:
# /etc/nginx/sites-available/next-app.conf
# Определение пула бэкенд-серверов Node.js
upstream nextjs_backend {
server 127.0.0.1:3000;
keepalive 64;
}
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Сжатие gzip для ускорения отдачи текста, JS и CSS
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml image/svg+xml;
# 1. Прямая отдача статических чанков Next.js с долгим кэшированием в браузере (1 год)
location /_next/static/ {
alias /var/www/my-next-app/.next/static/;
expires 365d;
access_log off;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# 2. Прямая отдача фавиконов, роботов и публичных картинок
location /public/ {
alias /var/www/my-next-app/public/;
expires 30d;
access_log off;
add_header Cache-Control "public, max-age=2592000";
}
# 3. Проксирование всех динамических запросов на локальный порт Node.js
location / {
proxy_pass http://nextjs_backend;
proxy_http_version 1.1;
# Поддержка WebSocket (Hot Reload, Server-Sent Events)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
# Передача реальных сетевых заголовков клиента
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# Таймауты ожидания ответа от SSR
proxy_read_timeout 60s;
proxy_connect_timeout 60s;
}
}
Почему заголовок «Cache-Control: immutable» безопасен и критически важен
При каждой сборке Next.js генерирует для JavaScript-чанков и CSS уникальные хэш-суммы содержимого в именах файлов (например, main-a1b2c3d4.js). Если вы обновите код сайта, имена изменившихся файлов автоматически изменятся. Заголовок Cache-Control: "public, max-age=31536000, immutable" указывает браузеру кэшировать эти ассеты ровно на 1 год и никогда не отправлять проверочные 304-запросы на сервер при обновлении страницы. Это ускоряет повторную загрузку сайта до 10–20 миллисекунд и снимает до 90% паразитной нагрузки с сервера.
Активируйте виртуальный хост и проверьте синтаксис Nginx:
# Создаем символическую ссылку
sudo ln -s /etc/nginx/sites-available/next-app.conf /etc/nginx/sites-enabled/
# Проверяем отсутствие синтаксических ошибок
sudo nginx -t
# Перезагружаем конфигурацию веб-сервера
sudo systemctl reload nginx
Сгенерируйте и сверьте готовые конфигурации веб-серверов в бесплатном интерактивном конструкторе SysKit:
8. Автоматический выпуск и продление SSL-сертификата Let's Encrypt
Установите Certbot и выпустите бесплатный TLS-сертификат с автоматическим перенаправлением всех посетителей с незащищенного HTTP на защищенный HTTPS:
# Установка Certbot и плагина для Nginx
sudo apt install -y certbot python3-certbot-nginx
# Выпуск сертификата с автоматическим редиректом
sudo certbot --nginx -d example.com -d www.example.com --redirect --non-interactive --agree-tos -m admin@example.com
Проверьте доступность портов и валидность выпущенного сертификата с помощью онлайн-утилит SysKit:
9. Сетевая безопасность: настройка фаервола UFW
По умолчанию порт Node.js приложения (3000) должен быть полностью закрыт от внешнего сканирования. Настройте межсетевой экран UFW, разрешив входящие подключения только для SSH, HTTP и HTTPS:
# Разрешаем стандартные входящие порты
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
# Активируем фаервол
sudo ufw --force enable
sudo ufw status verbose
10. Скрипт автоматического обновления проекта без простоя (Zero-Downtime Deploy)
Для быстрой выкатки свежих коммитов из Git без остановки сайта создайте в корне проекта исполняемый bash-скрипт deploy.sh:
#!/usr/bin/env bash
# /var/www/my-next-app/deploy.sh
set -e
echo "🚀 Начинаем процесс деплоя..."
cd /var/www/my-next-app
# 1. Загрузка свежих изменений из Git
git pull origin main
# 2. Установка зависимостей (только при изменении package-lock.json)
npm ci --prefer-offline
# 3. Сборка нового продакшен-бандла
npm run build
# 4. Копирование актуальной статики в standalone директорию
cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/
# 5. Мягкая перезагрузка кластера PM2 без потери входящих запросов
pm2 reload ecosystem.config.js --update-env
echo "✅ Проект успешно обновлен без простоя!"
Сделайте скрипт исполняемым: chmod +x deploy.sh. Теперь для выкатки обновлений достаточно выполнить ./deploy.sh на сервере или вызвать этот скрипт через SSH из пайплайна GitHub Actions.
Автоматизация деплоя через GitHub Actions
Узнайте, как настроить автоматический деплой сайта из репозитория GitHub на VDS по SSH с защитой секретов и zero-downtime развертыванием в нашей практической инструкции.
Открыть инструкцию по GitHub Actions для VPS