Skip to main content
A .NET application runs on Sevalla as an application: Sevalla builds an image from your Git repository with the .NET SDK, then runs the published assemblies as processes. The web process serves HTTP with ASP.NET Core’s Kestrel server, background workers run hosted services or queue consumers, and jobs run Entity Framework Core migrations before each deployment. C#, F# and Visual Basic projects all build the same way.

Framework guides

ASP.NET Core

Deploy a minimal API on .NET 10 with PostgreSQL, an EF Core migrations bundle and a health check.

Blazor

Deploy a Blazor Web App with interactive server rendering and Data Protection keys in PostgreSQL.

How .NET applications run on Sevalla

Every process (web, worker, cron job and job) runs from the same image, each with its own start command. See Processes.

Pin the .NET version

The project’s TargetFramework is the version pin: a project that targets net10.0 builds with the .NET 10 SDK and runs on the .NET 10 runtime, because .NET runs an application only on the major version it targets. Ingot reads the version from the first of these that’s set:
  1. The INGOT_DOTNET_VERSION environment variable, or versions.dotnet in ingot.json.
  2. sdk.version in global.json, with its rollForward policy.
  3. The newest TargetFramework (or TargetFrameworks) among the project and the projects it references.
  4. .NET 10, Ingot’s default line, when nothing is set.
Ingot builds .NET 10, 9 and 8. A framework it has no image for fails the build, for example Storefront.csproj targets net11.0, which this Ingot release has no image for. The SDK can be newer than the framework a project targets, never older, and the application always runs on the runtime of its TargetFramework. A line such as 10 builds on Microsoft’s 10.0 images, which move to each patch release, so every new build picks up security fixes. The build plan’s .NET row shows the version and where it came from. You don’t need a global.json. If you have one, Ingot applies it as the dotnet command does. Microsoft’s SDK images carry the newest feature band of each line, such as the .NET 10 SDK 10.0.403. When the rollForward policy of global.json doesn’t allow that SDK, Ingot downloads the newest SDK Microsoft still services that the policy allows, checks its SHA-512 checksum, and builds with it. With the default policy, this global.json allows the 10.0.1xx SDKs:
global.json
The build plan shows the SDK in its own row:
Build log
When no SDK that Microsoft services matches, the build fails, for example with global.json asks for SDK 10.0.200, and its rollForward policy doesn't allow 10.0.403 or 10.0.114, the .NET 10 SDKs Microsoft services. Set rollForward to latestFeature, or sdk.version to one of the SDKs the message lists. If global.json sets sdk.paths, include "$host$", so the SDK in the image can be used. See Set the language version.

Build with a Dockerfile

A Dockerfile for a .NET application on Sevalla builds in two stages: it restores and publishes with Microsoft’s SDK image, then copies only the published output into the ASP.NET Core runtime image of the same 10.0 line, which has the /bin/sh that start commands and the web terminal need. It runs as the image’s numeric app user (USER 1654), sets ASPNETCORE_FORWARDEDHEADERS_ENABLED=true, and points ASPNETCORE_HTTP_PORTS at the PORT variable in its command before it hands process 1 to dotnet, which handles SIGTERM itself. The guides show it whole: ASP.NET Core and Blazor.