Vercel делает деплой Next.js похожим на один git push. Это здорово — пока вам не нужен контроль над рантаймом, скучная машина или счёт, который не растёт вместе с трафиком клиента. Запустить Next.js на собственном VPS не сложно. Это рельса, которую вы прокладываете один раз, а потом git push вечно. Вот весь путь, готовый к копированию, с двумя подводными камнями, о которые действительно спотыкаются люди.
Что вы собираете
Стандартная продакшн-раскладка: ваше Next.js-приложение работает как долгоживущий Node-процесс на 127.0.0.1:3000, а Nginx стоит спереди, завершая TLS и проксируя в него. Вот и всё. Один сервер, одно приложение, ноль сложности с оркестрацией.
Internet → Nginx (:443, TLS) → Next.js server (:3000)
1. Возьмите сервер
Возьмите Ubuntu LTS VPS — 26.04 «Resolute Raccoon» — это текущий LTS, выпущен 23 апреля 2026.[1] Фиксируйте LTS, а не новейший промежуточный релиз.
- 1–2 ГБ RAM достаточно для небольшого приложения.
- Машина с 1 ГБ будет крутить Next.js, но уйдёт в своп во время сборки; 2 ГБ делают
next buildкомфортным. Мы не публикуем цифру пикового потребления памяти сборкой, пока не замерили её на чистой машине — проверьте своё приложение черезnext buildпод/usr/bin/time -v. - Выбирайте любого провайдера (DigitalOcean, Linode, Vultr, Hetzner). Если вы в основном крутите одно приложение, бюджет важнее бренда.
2. Сначала захлопните дверь
Прежде чем трогать Node, защитите машину. Это шаг, который пропускают и о котором жалеют в 3 часа ночи.
# от root: создаём пользователя deploy и даём ему sudo
adduser deploy
usermod -aG sudo deploy
# только вход по ключу
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh && chmod 600 /home/deploy/.ssh/authorized_keys
# файрвол: только SSH, HTTP, HTTPS
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable
Затем отключите root-логин по SSH и вход по паролю в /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
Перезапустите SSH (sudo systemctl restart ssh) и не закрывайте текущую сессию, пока не убедитесь, что новый вход работает.
3. Установите Node
Используйте nvm, чтобы фиксировать и переключать версии, а не зависеть от устаревшего Node дистрибутива.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
# перезагрузите шелл, затем:
nvm install 24
nvm use 22
Установите текущий Active LTS-мажор — Node 24 на сентябрь 2026; Node 22 перешёл в Maintenance LTS 23 сентября 2026.[2] Неподдерживаемый мажор меняет флаги сборки и поведение некоторых модулей.
Вам нужен LTS Node — и стабильный. Как только вы деплоите, конкретная версия Node становится частью контракта вашего приложения — зафиксируйте её в engines в package.json:
"engines": { "node": ">=22" },
4. Доставьте код на сервер
Три варианта, в порядке роста автоматизации:
Самый простой первый деплой — scp/rsync:
rsync -avz --exclude node_modules --exclude .next ./ deploy@your-server:/home/deploy/apps/myapp/
Чище — git clone из вашего репозитория. Приложению нужна собственная директория:
sudo mkdir -p /home/deploy/apps
sudo chown deploy:deploy /home/deploy/apps
git clone https://github.com/you/myapp.git /home/deploy/apps/myapp
cd /home/deploy/apps/myapp
5. Соберите его (первый большой подводный камень)
Ставьте и собирайте на сервере:
npm ci
npm run build
Две вещи делают это надёжным:
- Используйте
npm ci, неnpm install— он ставит из lockfile, воспроизводим и не даст зависимостям незаметно уехать в проде. - Задайте
output: 'standalone'вnext.config.js. Без этого вы потащите огромныйnode_modulesи сервер, которому придётся резолвить всё на рантайме. С ним Next.js собирает самодостаточную папку/.next/standaloneтолько с рантайм-кодом и нужными зависимостями.
// next.config.js
module.exports = {
output: 'standalone',
};
Когда запускаете из standalone, стартуйте сервер изнутри /.next/standalone — там свой крошечный node_modules. Это второй подводный камень: если запустить pm2 start из корня проекта, вы запустите не тот бандл и наткнётесь на странные ошибки путей.
6. Запустите через PM2
PM2 держит процесс живым, перезапускает его при падении и стартует при перезагрузке. Забудьте про nohup и голый node &.
npm i -g pm2
# из корня проекта, или из .next/standalone, если нужен плотный бандл:
pm2 start server.js --name myapp
Лучше: объявите его в ecosystem.config.js в корне проекта, чтобы конфиг версионировался вместе с приложением:
module.exports = {
apps: [{
name: 'myapp',
script: 'server.js', // если standalone: '.next/standalone/server.js'
cwd: '/home/deploy/apps/myapp',
instances: 1, // не ставьте 'max' бездумно на машине с 1 ГБ
exec_mode: 'fork',
env: { NODE_ENV: 'production', PORT: 3000 },
}],
};
Затем:
pm2 start ecosystem.config.js
pm2 save
pm2 startup # запустите напечатанную команду, чтобы включить старт при загрузке
Cluster-режим PM2 запускает несколько процессов и балансирует нагрузку между ними без правок кода.[3] На практике оставьте exec_mode: 'fork', пока вы не измерили, что один процесс не упирается в CPU; на небольшом VPS кластерный режим часто добавляет давление на память без видимой пользы для пользователя.
7. Nginx как входная дверь
Создайте /etc/nginx/sites-available/myapp:
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1: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;
}
}
Включите его и перезагрузите:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
proxy_http_version 1.1 плюс заголовки Upgrade/Connection — то, что позволяет WebSocket (и dev-режим HMR в Next.js) работать через прокси. Без них апгрейд соединения молча проваливается.
8. Бесплатный SSL через Let’s Encrypt
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com
sudo certbot renew --dry-run
Плагин Certbot для nginx переписывает блок сервера, добавляя TLS и редирект с :80. proxy_pass оставьте как есть — меняются только слои TLS.
9. Подключите автодеплой
Вы в одном git push от установки уровня deploysmith. GitHub Actions workflow, который заходит по SSH и передеплоивает:
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USER }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /home/deploy/apps/myapp
git pull
npm ci
npm run build
pm2 restart myapp
Если приложение собирается с output: 'standalone', Next.js пишет самодостаточную папку .next/standalone, которую вы запускаете через node .next/standalone/server.js;[4] сниппет выше предполагает server.js в корне проекта. Проверьте вашу точку входа, прежде чем фиксировать workflow.
Траблшутинг двух реально случающихся сбоев
502 Bad Gateway от Nginx. Прокси не может достучаться до 127.0.0.1:3000. Проверьте, что процесс реально слушает: pm2 list, затем curl http://127.0.0.1:3000. Обычно порт 3000 не занят, потому что сервер упал при загрузке.
Пути дают 404, но оболочка страницы загружается. Вы запустили не тот бандл. output: 'standalone' означает, что приложение должно стартовать из /.next/standalone, а папки .next/static и public должны быть скопированы рядом, иначе пути к ассетам не разрешатся. Если оставляете server.js на корневом уровне — откажитесь от standalone.
Итог «вот и всё»
VPS даёт тот же рантайм Next.js, что вы получили бы где угодно, плюс возможность задать версию Node, добавить кэш или запустить второй процесс. Путь короткий: защитите машину, поставьте Node, соберите с standalone, запустите через PM2, проксируйте через Nginx, TLS через Certbot. Единственные по-настоящему версионно-чувствительные детали — мажорная версия Node и настройки процесса PM2 — закрепите их, и остальное чисто механическое.
Источники
- Ubuntu 26.04 LTS «Resolute Raccoon» — анонс релиза Canonical (23 апр 2026). https://ubuntu.com/blog/2026/04/23/canonical-releases-ubuntu-26-04-lts-resolute-raccoon — проверено 2026-09-27
- Node.js Releases — график Active LTS / Maintenance. https://nodejs.org/en/about/previous-releases — проверено 2026-09-27
- PM2 — Cluster Mode (балансировка нагрузки между процессами). https://pm2.keymetrics.io/docs/usage/cluster-mode/ — проверено 2026-09-27
- Next.js —
output: 'standalone'(справочникnext.config.js). https://nextjs.org/docs/app/api-reference/config/next-config-js/output — проверено 2026-09-27