Docs / Programming & Development / ASP.NET Core on Linux

ASP.NET Core on Linux

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 131 views · 4 min read

ASP.NET Core is a high-performance, cross-platform framework that runs natively on Linux. It consistently benchmarks among the fastest web frameworks and is well-suited for enterprise APIs, microservices, and full-stack web applications. This guide covers deploying ASP.NET Core on a Linux VPS.

Installing .NET SDK

# Ubuntu 24.04
sudo apt install dotnet-sdk-8.0

# Or install specific version via Microsoft packages
wget https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt update && sudo apt install dotnet-sdk-8.0

# Verify
dotnet --version

Creating a Web API

# Create new Web API project
dotnet new webapi -n MyApi --use-controllers
cd MyApi

# Or minimal API (simpler, faster)
dotnet new webapi -n MyApi
cd MyApi

Minimal API Example

// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

// Routes
app.MapGet("/health", () => Results.Ok(new { Status = "healthy" }));

app.MapGet("/api/users", async (AppDbContext db) =>
    await db.Users.ToListAsync());

app.MapGet("/api/users/{id}", async (int id, AppDbContext db) =>
    await db.Users.FindAsync(id)
        is User user ? Results.Ok(user) : Results.NotFound());

app.MapPost("/api/users", async (CreateUserDto dto, AppDbContext db) =>
{
    var user = new User { Name = dto.Name, Email = dto.Email };
    db.Users.Add(user);
    await db.SaveChangesAsync();
    return Results.Created($"/api/users/{user.Id}", user);
});

app.Run();

Publishing for Production

# Self-contained deployment (includes .NET runtime)
dotnet publish -c Release -r linux-x64 --self-contained true \
    -p:PublishSingleFile=true -p:PublishTrimmed=true \
    -o ./publish

# Framework-dependent (smaller, requires .NET runtime on server)
dotnet publish -c Release -o ./publish

# Copy to VPS
scp -r publish/ user@vps:/opt/myapi/

Kestrel Configuration

// appsettings.Production.json
{
    "Kestrel": {
        "Endpoints": {
            "Http": {
                "Url": "http://localhost:5000"
            }
        },
        "Limits": {
            "MaxConcurrentConnections": 1000,
            "MaxRequestBodySize": 10485760,
            "RequestHeadersTimeout": "00:00:30"
        }
    },
    "ConnectionStrings": {
        "DefaultConnection": "Host=localhost;Database=myapi;Username=user;Password=pass"
    },
    "Logging": {
        "LogLevel": {
            "Default": "Warning"
        }
    }
}

Systemd Service

# /etc/systemd/system/myapi.service
[Unit]
Description=ASP.NET Core API
After=network.target

[Service]
Type=notify
User=www-data
WorkingDirectory=/opt/myapi
ExecStart=/opt/myapi/MyApi
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
Restart=always
RestartSec=10
SyslogIdentifier=myapi
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target

Nginx Reverse Proxy

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;

    location / {
        proxy_pass http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_connection;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

Docker Deployment

FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app

FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine
WORKDIR /app
COPY --from=build /app .
USER app
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApi.dll"]

Health Checks and Monitoring

// Program.cs
builder.Services.AddHealthChecks()
    .AddNpgSql(builder.Configuration.GetConnectionString("DefaultConnection")!)
    .AddRedis("localhost:6379");

app.MapHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

Performance Tips

  • Response caching: Use [ResponseCache] attributes and output caching middleware
  • Minimal APIs: 15-20% faster than controller-based APIs due to less middleware
  • Native AOT: .NET 8 supports ahead-of-time compilation for faster startup
  • Connection pooling: Npgsql pools by default; configure MaxPoolSize in connection string
  • Object pooling: Use ObjectPool<T> for frequently allocated objects

Summary

ASP.NET Core on Linux delivers enterprise-grade performance (consistently in the top 5 of TechEmpower benchmarks) with a mature ecosystem for authentication, authorization, data access, and real-time communication via SignalR. The self-contained publishing model simplifies deployment, and Kestrel behind Nginx provides a production-ready architecture. For teams with C# expertise, ASP.NET Core on a Linux VPS is one of the most performant and cost-effective deployment options available.

Was this article helpful?