Back to Blog
Tutorial9 min read2026-06-28

How to Deploy an ASP.NET Minimal API

A clean, end-to-end guide to deploying an ASP.NET Core Minimal API: container configuration, EF Core with a managed database, health checks, OpenAPI, and going from git push to a live HTTPS endpoint.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

ASP.NET Core Minimal APIs strip away controllers and boilerplate, letting you define endpoints inline. They're fast, low-ceremony, and produce small, focused services that are ideal for containers. This guide takes a Minimal API from project to a production HTTPS endpoint with a managed database wired in.

The shape of a Minimal API

A whole service can live in Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDb>(o =>
    o.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
builder.Services.AddHealthChecks();
builder.Services.AddOpenApi();

var app = builder.Build();
app.MapOpenApi();
app.MapHealthChecks("/health");

app.MapGet("/items", async (AppDb db) => await db.Items.ToListAsync());
app.MapPost("/items", async (Item item, AppDb db) =>
{
    db.Items.Add(item);
    await db.SaveChangesAsync();
    return Results.Created(
quot;/items/{item.Id}", item); }); app.Run();

That's a complete CRUD-ish service with OpenAPI and a health check. The deployment job is to containerize it correctly and connect a database.

Container configuration

The two settings that matter most are the listen URL and the connection string, both driven by environment variables. ASP.NET reads ASPNETCORE_URLS to decide where to bind:

ASPNETCORE_URLS=http://0.0.0.0:8080

Configuration keys map from environment variables using __ as the separator, so a connection string set as ConnectionStrings__Default is picked up by GetConnectionString("Default"). This is the clean way to inject secrets without touching appsettings.json.

Dockerfile

FROM mcr.microsoft.com/dotnet/sdk:8.0 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
WORKDIR /app
COPY --from=build /app .
ENV ASPNETCORE_URLS=http://0.0.0.0:8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "Api.dll"]

Copying the .csproj and restoring before copying the rest of the source is a layer-caching trick: as long as your dependencies don't change, Docker reuses the restore layer and rebuilds faster.

Database with EF Core

Define your DbContext and a model, then generate a migration locally:

dotnet ef migrations add InitialCreate

Apply migrations at startup for simple deployments:

using (var scope = app.Services.CreateScope())
    scope.ServiceProvider.GetRequiredService<AppDb>().Database.Migrate();

Npgsql uses key-value connection strings. If your host injects a URI-style DATABASE_URL, parse it once at startup:

var uri = new Uri(Environment.GetEnvironmentVariable("DATABASE_URL")!);
var userInfo = uri.UserInfo.Split(':');
var cs = 
quot;Host={uri.Host};Port={uri.Port};Database={uri.AbsolutePath.Trim('/')};" +
quot;Username={userInfo[0]};Password={userInfo[1]};SSL Mode=Require;Trust Server Certificate=true";

Deploying on PandaStack

With the Dockerfile committed:

  1. 1Connect your GitHub repo as a container app in the dashboard.
  2. 2The build runs in an ephemeral Kubernetes Job pod with rootless BuildKit, pushes the image to Google Artifact Registry, and deploys via Helm behind Kong ingress.
  3. 3Provision a managed PostgreSQL database (14.x or 16.x). PandaStack injects DATABASE_URL; set ConnectionStrings__Default to the parsed Npgsql string or parse the URL at startup as shown.
  4. 4Tail the live logs to confirm migrations ran and the app bound to the port.
StepWhat happens
git pushTriggers build
BuildKitBuilds image rootlessly, no Docker socket
Artifact RegistryStores the image
Helm deployRolls out the container
DB auto-wireDATABASE_URL injected
SSLAutomatic on the assigned domain

Minimal APIs are an excellent fit for the free tier. They start fast and have a small footprint, so the gVisor sandbox plus KEDA scale-to-zero on spot nodes works well: the service idles at zero cost and cold-starts quickly. For steady production traffic, move to a compute-optimized tier and keep a warm instance.

Verifying

curl https://your-app.pandastack.app/health
curl https://your-app.pandastack.app/items
curl https://your-app.pandastack.app/openapi/v1.json

Attach a custom domain and SSL is issued automatically. The server-side metrics view shows request rate and latency captured at the ingress, no client SDK required.

Production checklist

  • Set ASPNETCORE_ENVIRONMENT=Production so detailed error pages are off.
  • Use SSL Mode=Require on the database connection.
  • Add structured logging; ASP.NET's built-in logger writes to stdout, which the platform captures.
  • Keep migrations idempotent and test them against a staging database before production.

References

  • Minimal APIs overview: https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis
  • ASP.NET configuration and environment variables: https://learn.microsoft.com/aspnet/core/fundamentals/configuration/
  • EF Core migrations: https://learn.microsoft.com/ef/core/managing-schemas/migrations/
  • Npgsql connection string parameters: https://www.npgsql.org/doc/connection-string-parameters.html
  • .NET official Docker images: https://hub.docker.com/_/microsoft-dotnet-aspnet

Minimal APIs give you production-grade services with very little code, and they containerize cleanly. Try the push-to-live flow with an auto-wired PostgreSQL database on PandaStack's free tier at https://dashboard.pandastack.io

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also