Deployment.sh

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.

Engineer kneeling in a colocation aisle with a laptop on a crash cart connected to a dedicated server

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.

Top-down view of a developer's desk with a laptop terminal, a notebook with a hand-sketched diagram of boxes and arrows and a hardware security key

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.

bash
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.ShutdownTimeout if 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

PlanMonthlyYearly, per monthServersUptime checks
Starter49 USD24 USD35, every 5 min
Team199 USD99 USD1550, every 1 min
Agency499 USD249 USD60250, every 1 min
Enterprise1 499 USD749 USD2501 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.

Try the demo