Deploy dotnet app Linux servers with .NET 8, Kestrel and nginx
ASP.NET Core runs on Linux as fast as anywhere else, and a small Ubuntu server costs a fraction of a Windows host. Deployment makes the move simple: it installs the .NET 8 runtime and SDK, publishes your project into a release folder, runs Kestrel under systemd behind nginx or Caddy, and switches to the new release only after it passes a health check.
What Deployment detects in a .NET solution
A *.csproj (or a *.sln with several projects) marks a .NET repository. The TargetFramework element, for example net8.0, picks the runtime, and a global.json pins the SDK. Microsoft.NET.Sdk.Web identifies the web project to publish, while worker projects built on Microsoft.NET.Sdk.Worker become background services. Microsoft.EntityFrameworkCore.Design tells Deployment that EF Core migrations are part of the release, and the provider package (Npgsql, Pomelo MySQL or SQL Server) picks the database.
- .NET 8 runtime and SDK installed per site, newer versions side by side
- Web project and worker projects detected from the SDK type
- EF Core migrations bundled and applied before the switch
- Node LTS added when the project builds a SPA frontend
What Deployment installs for .NET on Linux
Provisioning hardens the server (firewall, key-only SSH, unattended security updates, swap), installs the .NET SDK and ASP.NET Core runtime from the official package feed, and sets up nginx or Caddy as the reverse proxy with TLS from Let's Encrypt. Kestrel listens on a private port. Deployment configures the forwarded headers so your app sees the real client IP and the https scheme, which matters for redirects, cookies and authentication callbacks. NuGet packages are cached in a shared folder, so restores after the first deploy take seconds.
A deploy script with dotnet publish
The process command is dotnet out/Web.dll with ASPNETCORE_URLS and ASPNETCORE_ENVIRONMENT set by Deployment. Configuration values use the double underscore convention (ConnectionStrings__Default), stored as encrypted environment variables and diffed before each deploy. Change any line in the panel, for example to run tests before publishing.
cd "$RELEASE_PATH" dotnet restore dotnet publish src/Web/Web.csproj -c Release -o out --no-restore dotnet ef migrations bundle -p src/Web -o out/efbundle --force ./out/efbundle --connection "$ConnectionStrings__Default"
systemd services, workers and scheduled tasks
The web app runs as a systemd unit of Type=notify when you add UseSystemd(), so systemd knows exactly when Kestrel is ready. Worker services built on BackgroundService run as their own units and receive the stop signal on deploy, giving StopAsync time to finish current work. Recurring tasks can live in Hangfire or Quartz.NET inside the app, or as cron jobs that run a console project, with every run's output kept in the cron log.
Zero downtime releases for ASP.NET Core
Each release is published into its own folder. Deployment starts the new Kestrel process on a fresh port, calls your health endpoint (map one with app.MapHealthChecks("/health")), and switches the proxy only after it answers Healthy. The old process receives SIGTERM, and the host's shutdown timeout lets open requests complete before it exits. A release that fails to publish, migrate or start never sees traffic. Earlier releases keep their published output, so rollback switches back in seconds. Details on zero downtime deployment and deploy from git.
- Raise
HostOptions.ShutdownTimeoutif requests or workers need longer than the default to finish - Keep data protection keys in a shared folder or database so logins survive a release switch
PostgreSQL, MySQL and Redis for .NET apps
PostgreSQL through Npgsql is the most common choice on Linux, and MySQL or MariaDB work through Pomelo. Deployment creates the database, user and grants, limits remote access to your app servers, and backs everything up to your own S3-compatible storage. Redis is ready for distributed cache, SignalR backplane and sessions. Apps tied to SQL Server can keep their existing database and connect to it from the Linux host.
Which plan fits your .NET deployments
| Plan | Monthly | Yearly, per month | Servers | Uptime checks |
|---|---|---|---|---|
| Starter | 49 USD | 24 USD | 3 | 5, every 5 min |
| Team | 199 USD | 99 USD | 15 | 50, every 1 min |
| Agency | 499 USD | 249 USD | 60 | 250, every 1 min |
| Enterprise | 1 499 USD | 749 USD | 250 | 1 000, every 30 s |
A single API with a worker fits Starter. A team moving several services from Windows hosts to Linux, with staging and production and roles for each developer, fits Team. Organizations that need SSO (SAML) and an SLA choose Enterprise.
Questions about .NET on Linux
Do I need to change my code to run on Linux?
Usually not. ASP.NET Core is cross-platform. Watch for file paths with backslashes, case-sensitive file names and Windows-only APIs such as the registry.
Can I run Blazor Server and SignalR?
Yes. The proxy forwards WebSocket connections to Kestrel, and Redis is available as a SignalR backplane when you run more than one instance.
Which .NET versions are supported?
.NET 8 is the default LTS. Newer releases install side by side, and each site picks its own runtime from the target framework.
Can I try Deployment before I pay?
Yes. The home page has a free one-time demo: paste a public repository URL and see the detected stack, the server plan and the first lines of the deploy script. Plans start at 49 USD a month, or 24 USD a month billed yearly.
Move your .NET apps to servers you control
Connect a Linux server and push your next release.
$ git push origin main
✓ build release 20261019-142210
✓ health check 200
✓ switch current 0.4 s
$