ASP.NET Core приложение можно выложить в сеть, не арендуя сервер: загружаете исходники вместе с файлом .csproj, платформа определяет .NET, собирает проект и выдаёт публичную HTTPS-ссылку. Условие одно, и оно решающее: приложение должно слушать адрес, который платформа передаёт в переменной ASPNETCORE_URLS, а не localhost и не порт, вписанный в код. HTTPS внутрь приложения не нужен — сертификат и шифрование обеспечивает платформа снаружи, а до контейнера запрос доходит по обычному http. Строку подключения и ключи API держите в переменных окружения, а не в appsettings.json. Ниже — путь от папки с кодом до рабочей ссылки на Dockbox.
Почему .NET-проект по умолчанию тащат на VPS
Привычный сценарий начинается с покупки ASP.NET Core хостинга: арендовать сервер, поставить нужную версию .NET, собрать проект, поставить перед ним nginx или IIS, выпустить сертификат и проследить, чтобы процесс поднялся после перезагрузки. Для одного сайта или небольшого C#-сервиса это пара вечеров, потраченных на всё, кроме самого приложения.
Нужная версия рантайма. .NET 6, 7 и 8 живут рядом, и несовпадение версии SDK с целевой платформой проекта — частая причина того, что сборка падает на чужой машине.
Обратный прокси и сертификат. Без nginx или IIS приложение не отдаёт HTTPS. Дальше — продление сертификата каждые три месяца, которое легко проспать.
Автозапуск и перезапуск. Чтобы приложение вернулось в строй после падения и перезагрузки сервера, нужен systemd-юнит или супервизор, и его отладка — отдельный навык.
Оплата независимо от нагрузки. Сервер стоит денег все 30 дней, даже если сайт почти не открывают.
Платформа деплоя снимает эту часть: вы загружаете папку с исходниками, она находит .csproj, ставит нужную версию .NET, собирает приложение и отдаёт публичную HTTPS-ссылку. Ни сервера, ни IIS, ни ручного сертификата.
Шаг 1. Отдайте исходники, а не результат dotnet publish
Сборка происходит на стороне платформы, поэтому нужен исходный код вместе с .csproj и файлом решения .sln, если проектов в решении больше одного. Папки bin и obj в архив не кладите: это результат сборки на вашей машине, они раздувают загрузку и иногда мешают. Пакеты NuGet тоже не нужно тащить — они восстанавливаются из списка зависимостей в файле проекта.
Локально это выглядит так, но выполнять команду перед загрузкой не требуется:
dotnet publish -c Release -o ./publish
Шаг 2. Слушайте адрес из ASPNETCORE_URLS
Адрес и порт задаёт платформа: значение приходит в переменной окружения ASPNETCORE_URLS, и Kestrel читает её сам, без единой строки кода с вашей стороны. Уберите всё, что это переопределяет: вызов UseUrls в коде, секцию Kestrel с портами 5000 и 5001 в appsettings.json, привязку к localhost или 127.0.0.1. Жёстко прописанный порт и привязка к localhost — причина номер один, по которой ссылка потом не открывается.
Плохой вариант:
builder.WebHost.UseUrls("http://localhost:5000");
Правильный — не указывать ничего и дать Kestrel прочитать переменную. Файл launchSettings.json на публикацию не влияет вообще: он нужен только при запуске на вашей машине.
Шаг 3. Не включайте HTTPS внутрь приложения
Защищённое соединение и сертификат берёт на себя платформа снаружи, а до приложения запрос доходит по простому http. Значит, сертификат разработчика, вызов UseHttpsRedirection и HTTPS-эндпоинт в настройках Kestrel внутри проекта не нужны: они либо ничего не делают, либо отправляют посетителей на адрес, которого внутри контейнера не существует, — и вы получаете цикл редиректов. Замок в браузере остаётся на месте, потому что его выдаёт платформа.
Исходную схему запроса, если она нужна, берите из заголовков X-Forwarded-Proto, а не редиректом внутри.
Шаг 4. Строку подключения и ключи — в переменные окружения
Не оставляйте строку подключения к базе, ключи сторонних API и токены в appsettings.json: этот файл едет вместе с кодом и попадает в репозиторий. В настройках проекта Dockbox запрашивает такие значения и подставляет их переменными окружения в зашифрованном виде. Конфигурация ASP.NET Core читает окружение поверх appsettings.json, а вложенные ключи разделяются двойным подчёркиванием:
ConnectionStrings__Default=Host=db;Database=app;Username=app;Password=***
Шаг 5. Решите, где живут данные
Файлы, которые приложение пишет рядом с кодом, пересоздаются при следующей публикации. Всё, что должно пережить обновление кода — загрузки пользователей, файл базы SQLite, — кладите в постоянную папку /data: её содержимое видно на вкладке Data в панели, откуда файлы можно скачать, загрузить или удалить. Если нужна полноценная база вроде PostgreSQL, запускайте её отдельным сервисом рядом с приложением через собственный docker-compose. Проекты со своим compose-файлом папку /data не получают: постоянным хранилищем там служат именованные тома из вашего файла.
Шаг 6. Загрузите код и получите ссылку
Проект попадает на платформу одним из двух способов: архивом с исходниками или импортом репозитория GitHub, в том числе приватного. Ставить .NET SDK у себя и запускать команды сборки не нужно. Логи и статус публикации видны в панели в реальном времени, поэтому ошибка сборки проявляется сразу, а не пустой страницей, и после успешного запуска вы получаете публичную HTTPS-ссылку.
Сколько это стоит и как работают контейнеры
Здесь принципиальная разница с VPS. Ни администрировать сервер, ни следить за процессом не нужно: контейнеры работают постоянно, без ограничения по часам и без «засыпания». Упавший контейнер платформа перезапускает сама, и приложение возвращается в строй без вашего участия.
Starter — 990 ₽/мес. 1 vCPU, 2 GB RAM, один проект. Первый месяц бесплатный. Для одного ASP.NET Core приложения этого достаточно, и лимита по времени работы у тарифа нет.
Pro — 2 490 ₽/мес. 2 vCPU, 4 GB RAM, до пяти проектов.
Team — 4 990 ₽/мес — для команд, которым нужен запас ресурсов и SLA.
Свой Dockerfile поддерживается, но не обязателен: язык и стек платформа определяет по коду сама. Если Dockerfile всё же нужен, берите готовые образы Microsoft — mcr.microsoft.com/dotnet/sdk для сборки и mcr.microsoft.com/dotnet/aspnet для рантайма:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
COPY . /src
RUN dotnet publish /src -c Release -o /app
FROM mcr.microsoft.com/dotnet/aspnet:8.0
COPY --from=build /app /app
ENTRYPOINT ["dotnet", "MyApp.dll"]
Частые вопросы
Нужно ли запускать dotnet publish перед загрузкой?
Нет, загружайте исходный код: сборка выполняется на нашей стороне из файла .csproj. Папки bin и obj включать не нужно — они только увеличивают архив. Установленный .NET SDK тоже не требуется.
Почему приложение не открывается по ссылке?
Чаще всего оно слушает не тот адрес: порт жёстко прописан в коде или в appsettings.json, либо привязка указывает на localhost вместо 0.0.0.0. Уберите свои значения и дайте Kestrel прочитать ASPNETCORE_URLS. Вторая частая причина — перенаправление на HTTPS внутри приложения, которое отправляет посетителей на несуществующий адрес. В логах панели видно, какая из двух.
Нужен ли Dockerfile и образ mcr.microsoft.com/dotnet/aspnet?
Нет. Платформа определяет .NET по файлу проекта и собирает приложение сама. Образы Microsoft (mcr.microsoft.com/dotnet/sdk и mcr.microsoft.com/dotnet/aspnet) нужны только при нестандартной сборке со своим Dockerfile.
Поддерживаются ли Blazor, Minimal API и MVC?
Да. Для платформы это обычное ASP.NET Core приложение, и шаблон проекта роли не играет. Blazor Server и размещённый с сервером Blazor WebAssembly работают так же, как MVC или Minimal API. Отдельный клиент Blazor WebAssembly без сервера публикуется как статический сайт.
Можно ли использовать SQLite и Entity Framework?
Да, но файл базы должен лежать в постоянной папке /data, иначе он пересоздастся при следующей публикации. Строку подключения держите в переменных окружения, а не в appsettings.json. Миграции применяйте при старте приложения: SSH-доступа в контейнер нет, запустить разовую команду негде.