🚀
Хостинг

Как вывести NestJS в production: сборка, PM2, Nginx и SSL

11.11.2026
← Все статьи

Локально NestJS работает очень удобно: npm run start:dev сам перезапускает сервер при изменении кода, а ошибки сразу видны в терминале. Но у production-среды совсем другие требования. Там приложение должно работать круглосуточно, само подниматься после перезагрузки сервера, хранить секреты отдельно от кода и общаться с внешним миром только по защищённому HTTPS. В этой статье мы пошагово выведем на VPS API небольшой службы доставки из Ташкента и разберём, на что обратить внимание на каждом этапе.

Какой сервер нужен: VPS или обычный хостинг

NestJS — это постоянно работающий процесс Node.js. В отличие от PHP-сайтов, он не запускается заново на каждый запрос, а слушает свой порт и держит фоновые задачи. Поэтому ему нужна среда, где можно запустить долгоживущий процесс. Самый надёжный вариант — VPS: вы сами выбираете версию Node.js, ставите PM2 или systemd и настраиваете Nginx как угодно. Возможность запускать Node.js на обычном shared-хостинге зависит от тарифа и настроек панели, поэтому перед стартом проекта это стоит уточнить в кабинете sayt.uz или через техподдержку.

В нашем примере API заказов работает по адресу api.example.uz. Он использует PostgreSQL, отправляет курьерам уведомления в Telegram и обменивается JSON с мобильным приложением. Сервер — VPS с Ubuntu, A-запись домена указывает на его IP-адрес.

Подготовка кода к production

Начните с файла main.ts. Приложение должно брать порт из переменной окружения, слушать только внутренний интерфейс и корректно обрабатывать сигналы остановки. Вызов enableShutdownHooks() позволяет аккуратно закрыть соединения с базой, когда PM2 или systemd останавливает процесс. А чтобы за Nginx видеть реальный IP клиента, нужно включить настройку Express trust proxy.

import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  app.set('trust proxy', 1);
  app.enableShutdownHooks();
  const port = Number(process.env.PORT ?? 3000);
  await app.listen(port, '127.0.0.1');
}
bootstrap();

Секреты — пароль базы, ключ JWT, токен Telegram-бота — никогда не должны лежать в коде или Git-репозитории. Стандартный путь — записать их в файл .env на сервере и читать через модуль @nestjs/config. Выполните chmod 600 .env, чтобы файл мог читать только пользователь, от имени которого работает приложение. Если файл лежит внутри веб-каталога, убедитесь, что Nginx не отдаёт его наружу.

Сборка и запуск на сервере

В production TypeScript напрямую не исполняется. Команда nest build компилирует его в обычный JavaScript в папке dist, и сервер запускает именно эти файлы. Для установки зависимостей лучше использовать npm ci вместо npm install: она ставит точные версии из package-lock.json, и приложение не сломается из-за неожиданного обновления пакета.

cd /var/www/api
git pull
npm ci
npm run build
npm prune --omit=dev
NODE_ENV=production node dist/main.js

Последняя команда нужна, чтобы проверить, что приложение вообще стартует. Если в терминале нет ошибок и curl http://127.0.0.1:3000 возвращает ответ, можно идти дальше. npm prune --omit=dev удаляет пакеты, нужные только для разработки, и экономит место на диске.

Миграции базы: откажитесь от synchronize

Опция synchronize: true в TypeORM удобна при разработке, но в production она сама меняет структуру таблиц и может привести к потере данных. На боевом сервере её нужно отключить и применять изменения через файлы миграций. В проектах на Prisma для этого есть отдельная команда: она выполняет только ещё не применённые миграции и не создаёт новых.

# Prisma
npx prisma migrate deploy

# TypeORM (если data-source собран в dist)
npx typeorm migration:run -d dist/data-source.js

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

Постоянная работа через PM2

Если закрыть терминал, процесс node dist/main.js остановится. Чтобы держать его в фоне, поднимать после падения и автоматически запускать при перезагрузке сервера, используем менеджер процессов PM2. Кластерный режим дополнительно запускает приложение на нескольких ядрах процессора и ускоряет ответы под нагрузкой.

npm install -g pm2
NODE_ENV=production pm2 start dist/main.js --name api -i 2
pm2 save
pm2 startup systemd
pm2 logs api --lines 50

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

Nginx как reverse proxy и HTTPS

Приложение слушает только 127.0.0.1:3000, а внешние запросы принимает Nginx и передаёт ему. Такая схема перекладывает на Nginx SSL, сжатие, статику и ограничения размера запросов, а NestJS занимается только бизнес-логикой. Если используются WebSocket, обязательно передавайте заголовки Upgrade.

server {
    listen 80;
    server_name api.example.uz;
    client_max_body_size 10m;

    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 nginx -t и перезагрузите конфигурацию через sudo systemctl reload nginx. Затем получите сертификат Let's Encrypt: sudo certbot --nginx -d api.example.uz. Certbot сам добавит блок для порта 443 и настроит таймер автоматического продления.

Проверки после запуска

Когда API заработал, обязательно пройдитесь по нескольким пунктам. Этот список закрывает самые частые проблемы первой недели:

Заранее договоритесь и о процессе выкладки обновлений. Простейший вариант: git pull, npm ci, npm run build, миграции и pm2 reload api. В кластерном режиме reload заменяет экземпляры по одному, поэтому пользователи не замечают перерыва во время обновления.

Похожие статьи

🐧 Какую ОС выбрать для VPS: Ubuntu, Debian или AlmaLinux 🖥️ Новый VPS: что сделать в первые 30 минут ⏱️ max_execution_time: как работать с долгими PHP-скриптами 🥟 Bun вместо Node.js: можно ли использовать и как запустить на сервере
🌐 Язык
🇺🇿 O'zbek 🇺🇿 Ўзбек 🇷🇺 Русский ✓ 🇬🇧 English