Как задеплоить Bun-приложение и выложить в интернет

Bun-приложение выкладывается в сеть без аренды сервера: вы отдаёте исходники, платформа видит рядом с package.json файл bun.lock, ставит зависимости через bun install, запускает приложение и выдаёт публичную HTTPS-ссылку. Bun задумывался как быстрая замена Node.js: он сам исполняет TypeScript, сам ставит пакеты и умеет поднимать HTTP-сервер без фреймворка. Проблема в том, что платформ, которые понимают Bun «из коробки», заметно меньше, чем для Node, поэтому большинство гайдов заканчиваются ручной настройкой на VPS. Ниже — что проверить в проекте, чтобы первая же публикация на Dockbox закончилась рабочей ссылкой, и где Bun-проекты спотыкаются чаще всего.

Почему Bun-проект по умолчанию тащат на VPS

Привычный сценарий начинается с аренды машины и установки рантайма вручную, а дальше начинается та часть, ради которой обычно и берут платформу.

Bun не ставят из системных пакетов. В большинстве дистрибутивов его до сих пор нет в репозиториях, поэтому установка идёт отдельной командой или скриптом. Обновлять рантайм на живой машине тоже придётся вам.

Автозапуск и перезапуск. Чтобы процесс поднялся после падения и после перезагрузки сервера, нужен systemd-юнит или супервизор — и отдельный навык его отладки.

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

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

Платформа деплоя снимает эту часть: она находит признак Bun, ставит зависимости, запускает приложение и отдаёт публичную HTTPS-ссылку. Ни сервера, ни nginx, ни ручного сертификата.

Шаг 1. Оставьте bun.lock рядом с package.json

Локфайл — главный признак того, что проект нужно собирать именно на Bun. В старых версиях это бинарный bun.lockb, в новых — текстовый bun.lock. Если файла нет, сначала установите зависимости у себя и заберите его вместе с кодом:

bun install

Локфайл ещё и фиксирует точные версии пакетов, поэтому сборка на сервере совпадёт с той, которую вы тестировали локально. Если получить файл никак не выходит, назовите Bun прямо в package.json: подойдёт либо start-скрипт, начинающийся с bun, либо поле engines со значением bun. Без любого из этих сигналов проект уедет как обычное Node-приложение на npm — и, скорее всего, не запустится.

Шаг 2. Оставьте одну понятную команду запуска

Добавьте в раздел scripts файла package.json запись start — например, bun run index.ts или bun run src/server.ts:

{
  "scripts": {
    "start": "bun run src/server.ts"
  }
}

Одна команда вместо трёх способов запуска снимает любые сомнения о точке входа. Если проект работает и на Node — это не страшно: важно, что команда запуска ровно одна и она очевидна.

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

Порт задаёт платформа, а не вы. Значение приходит в переменной окружения PORT, и его нужно прочитать в рантайме. Слушать нужно адрес 0.0.0.0, а не 127.0.0.1: иначе приложение отвечает только внутри контейнера, и ссылка остаётся пустой. Жёстко вписанный в код порт — причина номер один, по которой ссылка не открывается.

Плохой вариант — и порт, и адрес зафиксированы числами:

Bun.serve({
  port: 3000,
  hostname: "127.0.0.1",
  fetch(req) {
    return new Response("ok");
  }
});

Правильный — значения берутся из окружения:

const port = Number(Bun.env.PORT) || 3000;
Bun.serve({
  port,
  hostname: "0.0.0.0",
  fetch(req) {
    return new Response("ok");
  }
});

Elysia принимает порт в своём вызове listen, Hono экспортирует объект с полями fetch и port — в обоих случаях подставляйте значение из PORT, а не константу:

import { Elysia } from "elysia";
new Elysia()
  .get("/", () => "ok")
  .listen(Number(Bun.env.PORT) || 3000);

Фреймворк роли не играет: чистый Bun.serve, Elysia и Hono работают одинаково хорошо. Важно только одно — слушать правильный порт и правильный адрес.

Шаг 4. Ключи и токены — в переменные окружения

Строки подключения к базе, токены и ключи API читайте из окружения через Bun.env (или привычный process.env), а не держите константами в коде. Код вы почти наверняка зальёте в Git или кому-то покажете, и секреты уедут вместе с ним.

const token = Bun.env.API_TOKEN;
if (!token) throw new Error("API_TOKEN is not set");

В настройках проекта Dockbox такие значения задаются отдельно и передаются в контейнер в зашифрованном виде, поэтому в архиве и в репозитории они не появляются. Файл .env при этом включать не нужно.

Шаг 5. Не везите node_modules и результат сборки

Зависимости ставятся на нашей стороне из package.json и локфайла, поэтому папку node_modules в архив не кладите — загрузка останется маленькой, а версии пакетов совпадут с зафиксированными. Готовый бандл тоже не нужен: Bun исполняет TypeScript напрямую, отдельный шаг компиляции для этого не требуется. Если у проекта есть собственная сборка, просто вызовите её из команды запуска.

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

Проект попадает на платформу архивом или импортом репозитория GitHub, включая приватный. Дальше платформа ставит зависимости, запускает ваше Bun-приложение и выдаёт публичный HTTPS-адрес. Логи и статус публикации видны в панели в реальном времени, поэтому ошибка старта проявляется сразу, а не молчаливой пустой страницей. Обновление — тем же путём: свежий архив или синхронизация из GitHub, и ссылка остаётся прежней.

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

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

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

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

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

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

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

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

По локфайлу. bun.lock или bun.lockb рядом с package.json означает, что зависимости ставятся и приложение запускается через Bun. Если локфайла нет, годится упоминание Bun внутри package.json — start-скрипт, начинающийся с bun, или поле engines. Без этих сигналов проект уедет как обычное Node-приложение. Рантайм вручную в панели выбирать не нужно.

Что делать, если в проекте нет bun.lock?

Выполните bun install у себя и заберите созданный локфайл вместе с кодом: он заодно зафиксирует версии пакетов, чтобы сборка на сервере совпала с локальной. Если получить файл не удаётся, назовите Bun прямо в package.json — сработает и start-скрипт вида bun run index.ts. Когда нет ни того ни другого, зависимости поставятся через npm, и проект запустится как Node-приложение.

Работают ли Elysia и Hono?

Да, оба, как и приложение напрямую на Bun.serve. Требование ко всем одинаковое: слушать адрес 0.0.0.0 и порт из переменной окружения PORT. Специфичной настройки под конкретный фреймворк не требуется.

Ссылка не открывается. С чего начать?

В первую очередь проверьте порт и адрес: чаще всего порт вписан в код числом, а сервер слушает 127.0.0.1 вместо 0.0.0.0, и снаружи до него не достучаться. Вторая распространённая причина — падение на старте, например из-за незаданного секрета. И то и другое видно в логах панели в течение нескольких секунд после публикации.

Нужно ли компилировать TypeScript перед загрузкой?

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