Антон Ларичев
Деплой Next.js на VPS с Nginx и PM2
Введение
Деплой Next.js-приложения на собственный VPS даёт полный контроль над инфраструктурой: вы сами управляете ресурсами, настраиваете окружение и не зависите от платформ вроде Vercel или Netlify. В этой статье разберём полный цикл — от подготовки сервера до настройки HTTPS и автоматического перезапуска процессов.
Мы будем использовать:
- PM2 — менеджер процессов для Node.js, обеспечивает перезапуск при сбоях и запуск при старте сервера
- Nginx — веб-сервер и reverse proxy, принимает входящие запросы и проксирует их к Next.js
Курсы PurpleSchool
Учитесь у практикующих разработчиков — без воды
40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней
Подготовка сервера
Предполагается Ubuntu 22.04 LTS. Подключитесь по SSH и обновите пакеты:
sudo apt update && sudo apt upgrade -y
Установка Node.js
Рекомендуется устанавливать Node.js через NVM — это позволяет легко переключаться между версиями:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install 20
nvm use 20
nvm alias default 20
Проверьте установку:
node -v
npm -v
Установка PM2
npm install -g pm2
Установка Nginx
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx
Подготовка приложения
Сборка на локальной машине
Перед отправкой на сервер убедитесь, что приложение собирается без ошибок:
npm run build
Next.js создаст директорию .next с оптимизированными файлами. Для деплоя на VPS нужны:
.next/— результат сборкиpublic/— статические файлыpackage.jsonиpackage-lock.jsonnext.config.js(или.ts).env.production— переменные окружения
Передача файлов на сервер
Самый простой способ — клонировать репозиторий прямо на сервере. Сначала установите Git:
sudo apt install git -y
Затем клонируйте репозиторий:
cd /var/www
git clone https://github.com/your-username/your-project.git myapp
cd myapp
Альтернативно можно использовать rsync для синхронизации файлов без репозитория:
rsync -avz --exclude node_modules --exclude .git \
./ user@your-server-ip:/var/www/myapp/
Настройка приложения на сервере
Установка зависимостей и сборка
На сервере установите зависимости и соберите проект:
cd /var/www/myapp
npm ci --omit=dev
npm run build
Флаг --omit=dev пропускает devDependencies — на production-сервере они не нужны. npm ci в отличие от npm install строго следует package-lock.json, что обеспечивает воспроизводимость сборки.
Переменные окружения
Создайте файл .env.production на сервере:
nano /var/www/myapp/.env.production
Пример содержимого:
NODE_ENV=production
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
NEXTAUTH_SECRET=your-secret-key
NEXTAUTH_URL=https://yourdomain.com
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
Переменные с префиксом NEXT_PUBLIC_ встраиваются в бандл при сборке, поэтому они должны быть доступны во время npm run build. Серверные переменные (без префикса) читаются в runtime.
Настройка PM2
Запуск приложения
PM2 запускает Next.js как фоновый процесс:
cd /var/www/myapp
pm2 start npm --name "myapp" -- start
Проверьте статус:
pm2 status
pm2 logs myapp
Конфигурационный файл ecosystem
Для более гибкой настройки создайте файл ecosystem.config.js в корне проекта:
module.exports = {
apps: [
{
name: 'myapp',
script: 'node_modules/.bin/next',
args: 'start',
cwd: '/var/www/myapp',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000,
},
max_memory_restart: '1G',
error_file: '/var/log/pm2/myapp-error.log',
out_file: '/var/log/pm2/myapp-out.log',
merge_logs: true,
},
],
};
Ключевые параметры:
instances: 'max'— запускает по одному процессу на каждое ядро CPUexec_mode: 'cluster'— включает кластерный режим Node.js для распределения нагрузкиmax_memory_restart— автоматически перезапускает процесс при превышении лимита памяти
Запустите с использованием конфига:
pm2 start ecosystem.config.js
Автозапуск при перезагрузке сервера
pm2 startup
PM2 выведет команду, которую нужно выполнить с правами sudo. Выполните её, затем сохраните текущее состояние процессов:
pm2 save
Теперь PM2 будет автоматически запускать все сохранённые процессы при перезагрузке сервера.
Настройка Nginx
Конфигурация виртуального хоста
Создайте конфигурационный файл для вашего сайта:
sudo nano /etc/nginx/sites-available/myapp
Базовая конфигурация без HTTPS:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
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_cache_bypass $http_upgrade;
}
# Статические файлы Next.js
location /_next/static/ {
alias /var/www/myapp/.next/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Публичные файлы
location /public/ {
alias /var/www/myapp/public/;
expires 30d;
add_header Cache-Control "public";
}
}
Активируйте конфигурацию и перезагрузите Nginx:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Команда nginx -t проверит конфигурацию на ошибки перед применением.
Настройка HTTPS с Let's Encrypt
Установка Certbot
sudo apt install certbot python3-certbot-nginx -y
Получение SSL-сертификата
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot автоматически обновит конфигурацию Nginx для работы с HTTPS. После выполнения команды конфиг будет выглядеть примерно так:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
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_cache_bypass $http_upgrade;
}
location /_next/static/ {
alias /var/www/myapp/.next/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Автообновление сертификатов
Certbot автоматически создаёт задачу в cron. Проверьте, что автообновление работает:
sudo certbot renew --dry-run
Процесс обновления приложения
При каждом деплое новой версии выполните следующие шаги:
cd /var/www/myapp
# Получить последние изменения
git pull origin main
# Обновить зависимости
npm ci --omit=dev
# Пересобрать приложение
npm run build
# Перезапустить PM2 без downtime
pm2 reload myapp
Команда pm2 reload выполняет перезапуск с нулевым временем простоя — новые процессы запускаются до остановки старых.
Скрипт деплоя
Удобно вынести процесс деплоя в отдельный скрипт deploy.sh:
#!/bin/bash
set -e
APP_DIR="/var/www/myapp"
echo "Starting deployment..."
cd $APP_DIR
git pull origin main
npm ci --omit=dev
npm run build
pm2 reload ecosystem.config.js --update-env
echo "Deployment complete!"
Сделайте скрипт исполняемым:
chmod +x deploy.sh
Настройка файрвола
Откройте только необходимые порты:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
Nginx Full открывает порты 80 и 443. Порт 3000 (Next.js) закрыт снаружи — к нему обращается только Nginx изнутри сервера.
Мониторинг и отладка
Полезные команды PM2
# Просмотр статуса всех процессов
pm2 status
# Логи в реальном времени
pm2 logs myapp --lines 100
# Метрики процессора и памяти
pm2 monit
# Информация о конкретном процессе
pm2 show myapp
# Перезапуск с обновлением env-переменных
pm2 restart myapp --update-env
Просмотр логов Nginx
# Логи доступа
sudo tail -f /var/log/nginx/access.log
# Логи ошибок
sudo tail -f /var/log/nginx/error.log
Типичные проблемы
Приложение не запускается
Проверьте логи PM2:
pm2 logs myapp --err
Чаще всего причина — отсутствие переменных окружения или ошибка в сборке.
502 Bad Gateway в Nginx
Означает, что Nginx не может достучаться до Next.js. Проверьте:
# Запущен ли Next.js
pm2 status
# Слушает ли он нужный порт
ss -tlnp | grep 3000
Проблемы с правами доступа
На сервере файлы в /var/www должны принадлежать пользователю, от которого запускается PM2. Исправьте права:
sudo chown -R $USER:$USER /var/www/myapp
Заключение
В результате у вас получится надёжный production-стек:
- Next.js обрабатывает запросы на порту 3000
- PM2 управляет процессами, перезапускает их при сбоях и использует все ядра CPU
- Nginx принимает входящие запросы на 80/443 и проксирует их к Next.js, а также обслуживает статику с кешированием
- Let's Encrypt обеспечивает HTTPS с автообновлением сертификатов
Для углублённого изучения Next.js, включая продвинутые паттерны App Router, серверные компоненты и оптимизацию производительности, ознакомьтесь с курсом на PurpleSchool: https://purpleschool.ru/course/nextjs?utm_source=knowledgebase&utm_medium=text&utm_campaign=nextjs-deploy-vps