Деплой на VPS: SSH, nginx, systemd
Vercel — отлично для большинства. Но иногда нужен свой сервер: тяжёлые backgound-задачи, специфический софт, бот который должен слушать webhook 24/7, большие файлы. Тогда — VPS. Hetzner / DigitalOcean / TimeWeb / Selectel — от $4 за 2GB RAM. Разберём минимальный полный цикл.
Покупка VPS и первый вход
Заказываешь VPS — выбираешь Ubuntu 24.04 (или Debian 12). Получаешь IP-адрес и root-пароль. Первое:
→ вводишь пароль. Сразу: создай отдельного юзера, не сиди под root: adduser deploy, потом
Дальше залей свой ssh-ключ (
) и отключи вход по паролю в
PasswordAuthentication no. Это базовая безопасность.
Что ставим
обновить систему. Node.js:
PM2 для запуска:
nginx:
Postgres (если нужен):
Git:
Это весь стек минимального prod-сервера.
Деплой кода
Клонируешь репо:
(важно: не install, а ci — детерминированно из package-lock).
и заполнить реальными значениями.
Запуск через PM2:
PM2 поднимет процесс, перезапустит при падении, переживёт reboot (
+
).
nginx — публичный фасад
Твой Next.js крутится на :3000 локально. Снаружи нужно отдавать на :80 (HTTP) и :443 (HTTPS). Между ними — nginx как обратный прокси. Конфиг в
Симлинк в sites-enabled,
(проверка),
Готово.
systemd vs PM2
Альтернатива PM2 — systemd-юнит. Создаёшь
с
Restart=always.
Сложнее настройка, но интегрировано в систему — те же journalctl-логи, единый интерфейс. Для одного сервиса — нормально. Для 5+ — PM2 удобнее.
Обновления
Простейший флоу: ssh на сервер →
На один сайт — норм. Хочешь автоматизировать — GitHub Actions с SSH-ключом, который при push в main делает то же самое. Продвинуто — Docker + docker-compose, но это уже отдельная история.
# === ПЕРВЫЙ ВХОД (с твоего ноута) ===
ssh root@123.45.67.89
# === БЕЗОПАСНОСТЬ ===
# Создай юзера
adduser deploy
usermod -aG sudo deploy
# Залей свой ssh-ключ (с ноута, не с сервера)
ssh-copy-id deploy@123.45.67.89
# Запрети логин по паролю
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl reload ssh
# Настрой ufw (фаервол)
ufw allow ssh
ufw allow 80
ufw allow 443
ufw --force enable
# === УСТАНОВКА СТЕКА (теперь как deploy) ===
ssh deploy@123.45.67.89
sudo apt update && sudo apt upgrade -y
sudo apt install -y git nginx
# Node 22
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo bash -
sudo apt install -y nodejs
# PM2
sudo npm i -g pm2
# Postgres (если нужен)
sudo apt install -y postgresql
sudo -u postgres createuser deploy --createdb
sudo -u postgres createdb mysite_db
# === ДЕПЛОЙ ПРОЕКТА ===
sudo mkdir -p /var/www/site
sudo chown deploy:deploy /var/www/site
git clone https://github.com/me/mysite.git /var/www/site
cd /var/www/site
npm ci
cp .env.example .env
nano .env # заполни DATABASE_URL и т.п.
npm run build
# Запуск через PM2
pm2 start npm --name site -- start
pm2 save
pm2 startup # выполни команду которую он выдаст
# === NGINX ===
sudo nano /etc/nginx/sites-available/site
# Содержимое:
server {
listen 80;
server_name example.com www.example.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;
}
}
sudo ln -s /etc/nginx/sites-available/site /etc/nginx/sites-enabled/
sudo nginx -t # проверка конфига
sudo systemctl reload nginx
# === ОБНОВЛЕНИЕ ===
ssh deploy@123.45.67.89
cd /var/www/site
git pull
npm ci
npm run build
pm2 restart site
# === ЛОГИ И МОНИТОРИНГ ===
pm2 logs site # логи приложения
pm2 monit # cpu/memory
sudo journalctl -u nginx -f # логи nginx
df -h # сколько места на диске
free -h # сколько RAM свободно
htop # топ процессовПолный цикл: от пустого VPS до работающего сайта