Как выложить Node.js приложение в интернет

Node.js-приложение можно открыть в интернете, не арендуя сервер и не настраивая pm2 вместе с nginx: вы загружаете папку с кодом, платформа находит в ней package.json, ставит зависимости и отдаёт публичную HTTPS-ссылку. Но у Node.js есть свои привычки, из-за которых сервис после публикации либо не стартует, либо отвечает только изнутри контейнера. Ниже — путь от папки с кодом до рабочей ссылки на Dockbox и пять мест, где Node-проекты спотыкаются чаще всего.

Почему Node.js-сервис по умолчанию тащат на VPS

Привычный сценарий начинается с аренды виртуального сервера: поставить Node, запустить процесс, поставить перед ним обратный прокси, выпустить сертификат и придумать, как поднимать приложение после падения. Код при этом простой — а вся обвязка вокруг него ложится на вас.

Нужен процесс-менеджер. Запущенный вручную node server.js умирает вместе с закрытой сессией SSH, а после перезагрузки сервера не поднимается сам. Чтобы сервис жил дальше, ставят pm2 или пишут systemd-юнит — ещё одна сущность, которую надо настраивать и отлаживать.

Обратный прокси и сертификат. Без nginx приложение не отдаёт HTTPS, а сертификат нужно продлевать каждые три месяца и легко проспать эту дату.

Оплата сервера независимо от нагрузки. Машина стоит денег все 30 дней, даже если приложение отвечает пару раз в сутки.

Платформа деплоя снимает эту часть: вы загружаете код, она распознаёт Node.js по package.json, ставит зависимости, запускает процесс и отдаёт публичную HTTPS-ссылку. Ни сервера, ни pm2, ни ручного сертификата.

Шаг 1. Проверьте package.json: зависимости и одна команда запуска

package.json — это то, по чему платформа понимает, что перед ней Node-проект, что установить и чем запустить. Убедитесь, что в нём есть:

  • Список зависимостей — всё, что приложение импортирует в рантайме. Библиотеки для тестов и линтера держите в devDependencies.
  • Команда запуска в scripts.start — одна строка, которая поднимает сервис. Именно она исполняется в контейнере.
  • Версия Node в поле engines — чтобы сборка не уехала на другой мажорной версии.
{
  "name": "my-api",
  "engines": { "node": "20.x" },
  "scripts": {
    "build": "tsc",
    "start": "node dist/server.js"
  },
  "dependencies": {
    "express": "4.19.2"
  }
}

Поле engines и пустой scripts.start — две частые причины, по которым платформа не понимает, как запускать проект. Отдельный шаг сборки назовите build — он выполнится перед стартом.

Шаг 2. Ставьте зависимости через npm ci, а не npm install

На сервере не нужно угадывать версии: для этого в проекте лежит package-lock.json. Команда npm ci ставит ровно то, что записано в локфайле, и падает с ошибкой, если файл и package.json разошлись.

npm ci --omit=dev

Если локфайл отсутствует, платформа откатится к обычному npm install — а он подтянет свежие минорные версии пакетов и сделает сборку невоспроизводимой: сегодня собирается, завтра нет. Поэтому заберите package-lock.json вместе с кодом. Флаг --omit=dev экономит время и место только там, где шага сборки нет: если TypeScript компилируется при публикации, зависимости для сборки должны быть на месте.

Шаг 3. Читайте порт из PORT и слушайте 0.0.0.0

Это главная причина «сборка прошла, а ссылка не открывается». Порт в контейнере выдаётся переменной окружения, а не выбирается программой, и слушать нужно все интерфейсы, а не только локальный.

Так приложение доступно лишь внутри контейнера:

app.listen(3000, '127.0.0.1');

А так — снаружи:

const port = process.env.PORT || 3000;
app.listen(port, '0.0.0.0');

Правило одно для любого фреймворка — Express, Fastify, Koa, NestJS или голого модуля http. Если в коде жёстко вписан 3000, а платформа ждёт ответа на своём порту, запросы до приложения не дойдут.

Шаг 4. NODE_ENV=production и секреты — в переменные окружения

Переменная NODE_ENV управляет режимом самого Node и поведением фреймворков: в продакшене Express кеширует шаблоны, а часть библиотек отключает подробные сообщения об ошибках. Забыть её — значит запустить production-сервис с настройками разработки.

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

NODE_ENV=production
PORT=3000
DATABASE_URL=postgres://user:pass@db:5432/app
JWT_SECRET=change-me

В коде остаётся только чтение: process.env.DATABASE_URL. Приложение без нужной переменной обычно падает в первую же секунду — и в логах панели видно точную причину.

Шаг 5. Не везите node_modules и .env в архив

Папку node_modules собирать локально и загружать на сервер не нужно: зависимости всё равно ставятся заново из локфайла, а внутри лежат бинарные модули под вашу ОС и архитектуру. На сервере они бесполезны и только раздувают загрузку в сотни мегабайт.

Туда же — файл .env с секретами и результат сборки (папка dist, build). И то и другое создаётся при публикации из исходников. Добавьте в .gitignore строки node_modules/, .env, dist/ — иначе они попадут ещё и в Git-репозиторий.

Шаг 6. Загрузите код и получите ссылку

Проект можно отправить ZIP-архивом или подключить GitHub-репозиторий, включая приватный. Дальше платформа сама определяет Node.js, ставит зависимости и запускает приложение — ни Dockerfile, ни настройки процесса не нужны.

Статус и живые логи в панели показывают, на каком шаге сборка и что пишет процесс при старте. Если приложение упало, платформа перезапускает контейнер сама, а новая версия уезжает тем же путём — адрес остаётся прежним.

Сколько это стоит и как работают контейнеры

Администрировать сервер и следить за процессом не нужно: контейнеры работают постоянно, без ограничения по часам и без «засыпания». Упавший контейнер платформа перезапускает сама, а логи остаются в панели.

Starter — 990 ₽/мес. 1 vCPU, 2 GB RAM, один проект. Первый месяц бесплатный. Для одного Node.js-сервиса этого достаточно.

Pro — 2 490 ₽/мес. 2 vCPU, 4 GB RAM, до пяти проектов — если приложений несколько или нужен запас ресурсов.

Team — 4 990 ₽/мес — для команд, которым нужен запас и SLA.

Свой Dockerfile поддерживается, но не обязателен: язык и стек платформа определяет по коду сама. Тарифы — на dockbox.host.

Частые вопросы

Как платформа понимает, что проект надо запускать на Node.js, а не на Bun или Deno?

По package.json и локфайлу. Обычный package-lock.json или yarn.lock означает Node.js с npm или Yarn. Рантайм вручную в панели выбирать не нужно: если рядом лежит bun.lock или deno.json, проект уедет на другом движке, а без этих сигналов запустится как обычное Node-приложение.

Какую команду запуска указывать для Node.js-приложения?

Ту, что стоит в scripts.start — например node server.js или node dist/server.js для скомпилированного TypeScript. Если поле пустое, платформа попробует угадать точку входа, но лучше указать её явно. Отдельный pm2 внутри контейнера не нужен: процесс и его перезапуск контролирует платформа.

Сборка прошла, а ссылка не открывается. Что проверить?

Сначала порт и адрес: приложение должно слушать 0.0.0.0 и брать порт из process.env.PORT, а не из числа в коде. Вторая частая причина — падение сразу после старта: не задана обязательная переменная окружения, недоступна база или не сошлись версии в локфайле. И то и другое видно в логах панели вместе с текстом ошибки.

Что делать с node_modules и .env перед загрузкой?

Не включать в архив. node_modules пересобирается на сервере из локфайла, а бинарные модули внутри собраны под вашу ОС и там непригодны. Файл .env перенесите в переменные окружения панели и уберите из репозитория — иначе токены окажутся в истории Git.

Ссылка на Node.js-приложение открывается с задержкой после простоя. Почему?

У Dockbox контейнеры работают постоянно, поэтому «засыпания» быть не должно. Если первый запрос после паузы идёт долго, смотрите на код: обычно это ленивое подключение к базе или тяжёлый расчёт на старте — видно по времени ответа в логах панели.