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
MaxPoolSizein 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.