Rust-приложение можно выложить в сеть, не арендуя сервер: загружаете исходники вместе с Cargo.toml, платформа видит Rust-проект, собирает его и выдаёт публичную HTTPS-ссылку. Условие одно и оно решающее: приложение должно слушать адрес 0.0.0.0 и порт, который приходит в переменной PORT, а не 127.0.0.1 и не число, вписанное в код. И сразу настройтесь на главную особенность — первая сборка Rust длится дольше, чем у любого другого поддерживаемого языка: компилятор собирает каждую зависимость с нуля в release-режиме. Это норма, а не поломка. Ниже — путь от папки с кодом до рабочей ссылки на Dockbox и то, где Rust-проекты спотыкаются чаще всего.
Почему Rust-проект по умолчанию тащат на VPS
Привычный сценарий начинается с аренды сервера: поставить тулчейн, собрать проект, поставить перед ним nginx, выпустить сертификат и придумать, как поднимать процесс после падения и перезагрузки. Rust компилируется в один исполняемый файл — сам язык тут не сложный. Сложно всё, что вокруг файла, и обычно это ложится на вас.
Тулчейн и версия компилятора. rustup, edition, ночные сборки — всё это живёт на сервере, пока вы его не снесёте. Обновление Rust под новую зависимость может сломать сборку, и разбираться в этом придётся на живой машине.
Сборка на сервере. cargo build --release ест CPU и память, а линковка релизного бинарника легко требует больше гигабайта RAM. На дешёвом VPS процесс просто убивает OOM-killer, и вы видите невнятную ошибку вместо результата.
Обратный прокси и сертификат. Без nginx приложение не отдаёт HTTPS, а сертификат надо продлевать каждые три месяца.
Автозапуск и перезапуск. Чтобы бинарник вернулся в строй после падения и после перезагрузки, нужен systemd-юнит, и его отладка — отдельный навык.
Оплата сервера независимо от нагрузки. Машина стоит денег все 30 дней, даже если сервис дёргают раз в сутки.
Платформа деплоя снимает эту часть: вы загружаете папку с исходниками, она находит Cargo.toml, ставит тулчейн, собирает проект и отдаёт публичную HTTPS-ссылку. Ни сервера, ни nginx, ни ручного сертификата.
Шаг 1. Выберите фреймворк: Axum, Actix Web или Rocket
Для платформы выбор фреймворка не имеет значения: проект распознаётся по Cargo.toml, а не по названию библиотеки. Но выбирать всё равно придётся, поэтому коротко о каждом.
Axum — минималистичный фреймворк из экосистемы tokio, собранный на башнях-мидлварях. Хороший выбор для нового проекта: понятный API, отличная документация, легко подключать готовые мидлвари.
Actix Web — зрелый и очень быстрый, один из лидеров бенчмарков. Своя модель с акторами и App, чуть больше «магии», зато огромная база примеров.
Rocket — самый выразительный синтаксис, приятен для прототипов и небольших API. Нюанс один: конфигурацию с портом лучше собирать в коде, а не держать в Rocket.toml, иначе порт из окружения до приложения не доедет.
Требование к приложению одинаковое для всех трёх — адрес 0.0.0.0 и порт из переменной окружения. Всё остальное — вкусовщина.
Шаг 2. Отдайте исходники, а не папку target
Сборка идёт на стороне платформы, поэтому нужен исходный код: Cargo.toml, папка src и остальные файлы проекта. Папку target в архив не кладите — это результат вашей локальной сборки, она очень большая и на нашей стороне её всё равно пересоздадут.
Бинарник, скомпилированный на вашем ноутбуке, тоже не нужен: собранный под macOS или Windows, он на Linux-сервере не запустится вообще. А вот Cargo.lock оставьте в архиве — он фиксирует версии зависимостей, и сборка получится воспроизводимой.
Складывайте архив так, чтобы Cargo.toml оказался в корне, а не внутри лишней папки-обёртки.
Шаг 3. Читайте порт из PORT и слушайте 0.0.0.0
Порт задаёт платформа, а не вы. Значение приходит в переменной PORT, и его нужно прочитать в рантайме. В Rust это std::env::var, а адрес для привязки — 0.0.0.0, иначе приложение отвечает только внутри контейнера и ссылка останется пустой.
Плохой вариант — порт и адрес вписаны в код:
let listener = TcpListener::bind("127.0.0.1:8000").await?;
Правильный — адрес собирается из переменной окружения:
let port: u16 = env::var("PORT")?.parse()?;
let addr = format!("0.0.0.0:{port}");
let listener = TcpListener::bind(addr).await?;
В Axum это адрес в TcpListener::bind, в Actix Web — аргумент метода bind у HttpServer. С Rocket чуть иначе: конфиг удобнее собрать в коде, чем править Rocket.toml:
let port: u16 = env::var("PORT")?.parse()?;
let config = rocket::Config {
port,
address: "0.0.0.0".parse()?,
..Default::default()
};
rocket::custom(config).launch()
Шаг 4. Ключи и токены — в переменные окружения
Ключи API, токены и строки подключения к базе читайте через std::env::var, а не держите константами в коде. Код вы почти наверняка зальёте в Git или кому-то покажете, а вместе с ним уедут и секреты.
let db_url = env::var("DATABASE_URL")
.expect("DATABASE_URL is not set");
В настройках проекта Dockbox запрашивает такие значения и передаёт их в контейнер в зашифрованном виде, поэтому в архиве и в репозитории они не появляются. Файл .env при этом включать не нужно.
Шаг 5. Загрузите код и дождитесь первой сборки
Проект попадает на платформу архивом или импортом репозитория GitHub, в том числе приватного. Дальше начинается самое честное предупреждение этого материала: первая сборка Rust-проекта — самая долгая из всех поддерживаемых языков. Компилятор собирает не только ваш код, но и каждую зависимость с нуля, в release-режиме с оптимизациями. Сборка на несколько минут — это ожидаемое поведение, а не признак, что что-то сломалось.
Статус и живые логи в панели показывают, что сборка идёт. Повторные сборки обычно бывают быстрее: часть зависимостей уже закеширована. Новая версия уезжает тем же путём — свежий архив или обновление из GitHub, а ссылка остаётся прежней.
Сколько памяти нужно Rust-сервису и сколько это стоит
Это статически скомпилированный бинарник без интерпретатора и виртуальной машины, поэтому в простое веб-сервис на Axum или Actix ест десятки мегабайт, а не сотни. Основной аппетит приходится на этап компиляции: линковка release-бинарника может потребовать больше гигабайта RAM, и на слабой машине именно сборка, а не работа, упирается в потолок.
Ни администрировать сервер, ни следить за процессом не нужно: контейнеры работают постоянно, без ограничения по часам и без «засыпания». Упавший контейнер платформа перезапускает сама.
Starter — 990 ₽/мес. 1 vCPU, 2 GB RAM, один проект. Первый месяц бесплатный. Для одного Rust-сервиса этого достаточно, и лимита по времени работы у тарифа нет.
Pro — 2 490 ₽/мес. 2 vCPU, 4 GB RAM, до пяти проектов — вариант, если сборка упирается в память или приложений несколько.
Team — 4 990 ₽/мес — для команд, которым нужен запас ресурсов и SLA.
Свой Dockerfile поддерживается, но не обязателен: язык и стек платформа определяет по коду сама. Если Dockerfile нужен, берите многоступенчатую сборку, чтобы образ остался маленьким:
FROM rust:1.83 AS build
WORKDIR /src
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
COPY --from=build /src/target/release/myapp /usr/local/bin/myapp
CMD ["myapp"]
Частые вопросы
Почему первая сборка Rust идёт так долго?
Компилятор собирает не только ваш код, но и каждую зависимость с нуля, в release-режиме с оптимизациями, — поэтому это самая долгая сборка из всех поддерживаемых языков. Это ожидаемо: прогресс видно в логах панели. Последующие сборки обычно заметно быстрее.
Нужно ли компилировать бинарник самому и загружать файл?
Нет, загружайте исходники: Cargo.toml, папку src и остальные файлы проекта. Сборка происходит на нашей стороне, поэтому Rust и тулчейн локально не нужны. Бинарник, собранный на macOS или Windows, на сервере не запустился бы в любом случае.
Работают ли Axum, Actix Web и Rocket?
Да, фреймворк может быть любым: проект распознаётся по Cargo.toml, а не по названию библиотеки. Требование одно для всех — слушать 0.0.0.0 и порт из переменной PORT. С Rocket проще всего собрать конфигурацию в коде, а не через Rocket.toml.
Что делать, если в проекте несколько бинарников?
Укажите главный через default-run в Cargo.toml, чтобы не было двусмысленности с тем, что запускать. То же самое касается проекта из нескольких пакетов: организуйте его так, чтобы точка входа была одна. Иначе сборка может выбрать не тот бинарник.
Сборка прошла, а ссылка не открывается. Что проверить?
Начните с порта и адреса: приложение должно слушать 0.0.0.0 и порт из переменной PORT, а не 127.0.0.1 и число из кода. Вторая частая причина — падение сразу после старта, например из-за отсутствующего ключа или недоступной базы. И то и другое видно в логах панели, вместе с текстом ошибки и моментом остановки процесса.