Deno-приложение можно выложить в сеть, не арендуя сервер: загружаете исходники вместе с deno.json, платформа распознаёт Deno, тянет зависимости и выдаёт публичную HTTPS-ссылку. Условие простое и решающее: приложение должно читать порт из переменной PORT через Deno.env.get("PORT") и слушать 0.0.0.0, а не localhost и не число, вписанное в код. Но главная особенность Deno — разрешения: по умолчанию программа не имеет доступа ни к сети, ни к переменным окружения, поэтому в команде запуска обязательны флаги --allow-net и --allow-env. Без них контейнер собирается и тут же падает на старте с ошибкой вида «Requires net access». Ниже — путь от папки с кодом до рабочей ссылки на Dockbox.
Почему Deno-проект по умолчанию тащат на VPS
Привычный сценарий начинается с аренды сервера: поставить Deno, проверить версию, поставить перед приложением nginx, выпустить сертификат и придумать, как поднимать процесс после падения и перезагрузки. Deno — это один бинарник, и сам язык тут не сложный. Сложно всё, что вокруг него.
Чем Deno отличается от Node.js. В Node.js программа по умолчанию имеет доступ ко всему: сеть, файлы, переменные окружения, порты. Deno перевёрнут наоборот — доступа нет ни к чему, пока не попросите его флагом. Зависимости Deno берёт из URL и import map, поэтому папки node_modules в проекте обычно нет, а манифест называется deno.json, а не package.json. TypeScript работает из коробки, без отдельного шага сборки. На VPS эта же строгость выходит боком: флаги разрешений вы набираете руками в терминале, а после перезагрузки процесс поднимает systemd по юниту, в который их нужно аккуратно перенести. Забыли --allow-net — сервис умирает молча, и причину вы ищете в коде, хотя она в команде запуска.
Версия рантайма. Deno 1.x и 2.x живут рядом, и код, написанный под одну ветку, на другой может ругаться на изменившиеся API вроде Deno.serve.
Обратный прокси и сертификат. Без nginx приложение не отдаёт HTTPS, а сертификат надо продлевать каждые три месяца, и легко это проспать.
Автозапуск и перезапуск. Чтобы процесс вернулся в строй после падения и после перезагрузки, нужен systemd-юнит, и его отладка — отдельный навык.
Оплата независимо от нагрузки. Сервер стоит денег все 30 дней, даже если сервис дёргают раз в сутки.
Платформа деплоя снимает эту часть: вы загружаете папку с исходниками, она находит deno.json, ставит рантайм, скачивает зависимости и отдаёт публичную HTTPS-ссылку. Ни сервера, ни nginx, ни ручного сертификата.
Шаг 1. Положите deno.json в корень проекта
Именно файл deno.json (или deno.jsonc) в корне и говорит платформе: это Deno, а не Node.js. В нём же живут задача запуска и import map. Держите его в корне архива, рядом с главным файлом приложения, а не во вложенной папке — иначе язык определится неверно. Добавить файл стоит даже проекту из одного файла без внешних библиотек: он задаёт, как приложение стартует.
Складывайте архив так, чтобы deno.json оказался в корне, а не внутри лишней папки-обёртки.
Шаг 2. Читайте порт из PORT и слушайте 0.0.0.0
Порт задаёт платформа, а не вы. Значение приходит в переменной окружения PORT, поэтому берите его через Deno.env.get("PORT"), а не пишите число руками. Слушать нужно 0.0.0.0: адрес localhost виден только самому приложению изнутри, и публичная ссылка останется пустой. В Deno.serve это объект настроек с полями port и hostname, куда вы подставляете значение из окружения.
Плохой вариант — порт и адрес вписаны в код:
Deno.serve({ port: 8000, hostname: "localhost" }, handler);
Правильный — оба значения берутся из окружения:
const port = Number(Deno.env.get("PORT"));
Deno.serve({ port, hostname: "0.0.0.0" }, handler);
Жёстко прописанный порт — причина номер один, по которой деплой проходит, а страница потом не открывается.
Шаг 3. Выдайте права в команде запуска
Deno не пустит программу в сеть и не даст ей прочитать переменные окружения, пока вы не разрешите это флагами. Опишите команду запуска как задачу start в deno.json и перечислите в ней нужные разрешения — минимум --allow-net, чтобы принимать запросы, и --allow-env, чтобы читать порт и ключи:
{
"tasks": {
"start": "deno run --allow-net --allow-env main.ts"
}
}
Если приложение работает с файлами, добавьте --allow-read и --allow-write. Разбираться с точным списком сразу не обязательно: --allow-all для начала подойдёт, сузить можно позже.
Шаг 4. Не включайте кеш зависимостей в архив
Библиотеки Deno скачивает во время сборки сам — по импортам в коде и import map из deno.json. Поэтому папку кеша, каталог vendor или node_modules в архив класть не нужно: они тяжёлые, устаревают после первой правки и ничего не меняют. А вот файл deno.lock оставьте — он фиксирует те же версии библиотек, что вы проверяли локально, и сборка получается воспроизводимой.
Шаг 5. Храните ключи в переменных окружения
Токены, ключи сторонних API и строки подключения к базе читайте через тот же Deno.env.get, что и порт, — никогда не держите их константами в коде:
const token = Deno.env.get("API_TOKEN");
Код вы почти наверняка зальёте в Git или кому-то покажете, а вместе с ним уедут и секреты. В настройках проекта Dockbox запрашивает такие значения и передаёт их в контейнер в зашифрованном виде, поэтому в архиве и в репозитории они не появляются. Помните: чтение окружения требует флага --allow-env, иначе приложение упадёт при первой же попытке достать ключ.
Шаг 6. Загрузите код и получите ссылку
Проект попадает на платформу одним из двух способов: архивом с исходниками или импортом репозитория GitHub, в том числе приватного. Ставить Deno у себя и готовить сборку заранее не нужно — сборка идёт на нашей стороне, а её статус и логи видны в панели в реальном времени. Когда сборка закончится, вы получаете публичную HTTPS-ссылку, которую можно открыть и передать другим, и она остаётся той же при обновлении кода.
Сколько это стоит и как работают контейнеры
Ни администрировать сервер, ни следить за процессом не нужно: контейнеры работают постоянно, без ограничения по часам и без «засыпания». Упавший контейнер платформа перезапускает сама, и приложение возвращается в строй без вашего участия.
Starter — 990 ₽/мес. 1 vCPU, 2 GB RAM, один проект. Первый месяц бесплатный. Для одного Deno-сервиса этого достаточно, и лимита по времени работы у тарифа нет.
Pro — 2 490 ₽/мес. 2 vCPU, 4 GB RAM, до пяти проектов — вариант, если приложений несколько.
Team — 4 990 ₽/мес — для команд, которым нужен запас ресурсов и SLA.
Свой Dockerfile поддерживается, но не обязателен: язык и стек платформа определяет по коду сама. Если Dockerfile всё же нужен, берите официальный образ Deno:
FROM denoland/deno:2.1.4
WORKDIR /app
COPY . .
RUN deno cache main.ts
CMD ["run", "--allow-net", "--allow-env", "main.ts"]
Частые вопросы
Приложение стартует, а ссылка не открывается — почему?
Обычно дело в одном из трёх: приложению не выдали доступ к сети флагом --allow-net, порт вписан числом вместо значения из переменной PORT, либо сервер слушает localhost вместо 0.0.0.0. Проверьте задачу start в deno.json и строку, где создаётся сервер. Логи в панели показывают, чем закончился запуск, — гадать не нужно.
Что значит ошибка «Requires net access»?
Так Deno сообщает, что программа попыталась выйти в сеть или прочитать окружение без разрешения. Чинится это не в коде, а в команде запуска: добавьте нужные флаги — --allow-net для сети, --allow-env для переменных окружения, --allow-read и --allow-write, если приложение работает с файлами. Загрузите обновлённый код — проект пересоберётся на той же ссылке.
Как правильно передать порт в Deno.serve?
Deno.serve принимает объект настроек: в поле port подставьте значение из окружения, например Number(Deno.env.get("PORT")), а в hostname — 0.0.0.0. Если этого не сделать, Deno.serve возьмёт свой порт по умолчанию, и снаружи приложение будет недоступно.
Можно ли использовать npm-пакеты в Deno-проекте?
Да. Deno импортирует npm-пакеты напрямую, и мы скачиваем их во время сборки, поэтому node_modules в архиве не нужен. Если проект написан как обычное Node.js-приложение с package.json, он тоже публикуется — просто определится как Node.js. Не держите оба манифеста в одном корне: тогда определение языка станет непредсказуемым.
Сохраняются ли файлы, которые пишет приложение, между деплоями?
Только если писать их в постоянную папку /data. Файлы, созданные рядом с кодом, пересоздаются при следующей публикации. Для записи нужно разрешение --allow-write, а содержимое папки видно в панели на вкладке Data, откуда файлы можно скачать, загрузить или удалить.