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

Svelte-приложение после сборки — это набор статических файлов, а SvelteKit умеет и статику, и серверный рендеринг. От этого зависит всё остальное в деплое: что собирает платформа и как проект отвечает на запросы. Одна ловушка при этом попадается чаще прочих — адаптер: без правильного значения в svelte.config.js сборка останавливается с сообщением «could not detect a supported production environment». Ниже — путь от папки с кодом до рабочей HTTPS-ссылки на Dockbox и то, что стоит поправить в конфигурации заранее.

Почему деплой Svelte упирается в сборку и адаптер

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

Сборка перед каждой загрузкой. Браузер не исполняет .svelte-файлы, ему нужны готовые JS и CSS, поэтому без шага сборки деплой не начинается.

Адаптер. У SvelteKit в конфигурации стоит adapter-auto, который угадывает хостинг по окружению и на незнакомой площадке останавливает сборку.

SSR или статика. SvelteKit собирает либо пачку prerendered-страниц, либо Node-приложение, которое отвечает на запросы. Это два разных режима запуска.

Секреты в бандле. Всё, что попадает во фронтенд-сборку, видно любому посетителю в исходнике страницы, и ключи оказываются там быстрее, чем кажется.

Порт и адрес. Приложение должно слушать порт из переменной окружения и адрес 0.0.0.0, а не localhost.

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

Шаг 1. Определите, что у вас: Svelte или SvelteKit

Откройте package.json. Если в зависимостях только svelte и vite, а сборка кладёт результат в папку dist, это обычное Svelte-приложение, и результат его сборки — статический сайт: страницы, скрипты, стили. Если в зависимостях есть @sveltejs/kit, у вас SvelteKit, и ему нужно явно сказать, во что собираться. Отличается ровно один шаг, всё остальное одинаково.

Шаг 2. Выберите адаптер: статика или Node

Адаптер — строка конфигурации в svelte.config.js, которая говорит сборке, что производить на выходе.

Нет серверной части — ни данных, загружаемых на сервере, ни форм, обрабатываемых на сервере: берите adapter-static и включите пререндеринг в корневом layout-файле.

import adapter from '@sveltejs/adapter-static';
export default { kit: { adapter: adapter() } };
export const prerender = true;

Серверная часть есть — серверные маршруты, form actions, данные, которые браузер видеть не должен: нужен adapter-node. Он пишет готовый сервер в папку сборки, запускается командой node build, поэтому добавьте её скриптом запуска.

import adapter from '@sveltejs/adapter-node';
export default { kit: { adapter: adapter() } };
"scripts": {
  "build": "vite build",
  "start": "node build"
}

Шаг 3. Уберите «чужие» адаптеры

Свежий проект SvelteKit идёт с adapter-auto. Он определяет хостинг по окружению и на незнакомой площадке останавливает сборку сообщением о неподдерживаемом production-окружении. Адаптеры под конкретный сервис — vercel, netlify, cloudflare — не соберутся по той же причине: они готовят результат под чужую платформу, а не под обычный сервер.

Замените их на node или static: это штатные адаптеры SvelteKit, и они работают на любом обычном сервере, в том числе у нас. Плохой вариант:

import adapter from '@sveltejs/adapter-auto';

Правильный:

import adapter from '@sveltejs/adapter-node';

Шаг 4. Порт приложение берёт из переменной PORT

Приложение должно слушать порт, который платформа передаёт в переменной окружения PORT, и адрес 0.0.0.0, а не localhost. Адаптер для Node уже делает это сам, поэтому в коде порт не задаётся — следите только, чтобы в команде запуска не оказалось зашитого числа. Жёстко прописанный порт — причина номер один, по которой сборка проходит, а ссылка открывается пустой страницей. Со статическим адаптером порт вообще не важен: отдаются обычные файлы.

Шаг 5. Секреты — в переменные окружения, публичное — с префиксом PUBLIC_

Токены, ключи сторонних API и строку подключения к базе не держите в коде и в .env внутри архива. Платформа запрашивает эти значения при настройке проекта и подставляет их переменными окружения, храня в зашифрованном виде. В серверном коде SvelteKit значения читаются из окружения обычным способом, а всё, что нужно браузеру, обязано иметь префикс PUBLIC_ — без него переменная до клиента просто не доедет.

DATABASE_URL=postgres://app:***@db:5432/app
PUBLIC_API_URL=https://api.example.com

Помните, что значения с префиксом PUBLIC_ запекаются в готовые файлы: адрес API там уместен, настоящий ключ — нет.

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

Проект попадает на платформу архивом или импортом репозитория GitHub, включая приватный. node_modules, dist, build и .svelte-kit в архив не кладите: зависимости платформа ставит сама, а сборку выполняет на своей стороне, так что загрузка остаётся лёгкой. Логи и статус сборки видны в панели в реальном времени, а после успешного запуска вы получаете публичную HTTPS-ссылку.

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

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

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

Pro — 2 490 ₽/мес. 2 vCPU, 4 GB RAM, до пяти проектов.

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

Свой Dockerfile поддерживается, но не обязателен: стек платформа определяет по коду сама. Нужна база рядом с приложением — поднимите её отдельным сервисом в своём docker-compose с именованным томом, чтобы данные пережили обновление кода.

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

Чем деплой SvelteKit отличается от обычного Svelte?

Обычное Svelte-приложение собирается в папку файлов, которую достаточно отдать как статический сайт. SvelteKit умеет и так: он собирает либо prerendered-страницы, либо Node-приложение с серверной частью. Что именно получится, решает адаптер в конфигурации.

Какой адаптер SvelteKit выбрать?

Если весь сайт можно отрисовать заранее и ничего не считается под конкретный запрос — adapter-static, такие страницы загружаются быстрее всего. Если есть серверные маршруты, обработка форм на сервере или данные, которые не должен видеть браузер, — adapter-node. Оба работают на обычном сервере и на нашей платформе.

Почему сборка падает на adapter-auto?

Этот адаптер определяет хостинг по окружению и знает лишь несколько конкретных сервисов. На любом другом сервере он не находит знакомых признаков и останавливает сборку сообщением о том, что не смог определить поддерживаемое production-окружение. Лечится заменой на adapter-node или adapter-static.

Почему внутренние страницы дают 404 при обновлении?

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

Нужно ли ставить Node.js самому и можно ли подключить базу?

Ставить и настраивать Node.js не нужно: платформа узнаёт проект по package.json и поднимает всё сама. С базой тоже нет проблем: можно взять внешнюю и передать строку подключения секретом либо поднять базу рядом сервисом в своём docker-compose — данные в именованном томе переживут обновление кода.