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:8080Configuration 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 InitialCreateApply 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:
- 1Connect your GitHub repo as a container app in the dashboard.
- 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.
- 3Provision a managed PostgreSQL database (14.x or 16.x). PandaStack injects
DATABASE_URL; setConnectionStrings__Defaultto the parsed Npgsql string or parse the URL at startup as shown. - 4Tail the live logs to confirm migrations ran and the app bound to the port.
| Step | What happens |
|---|---|
| git push | Triggers build |
| BuildKit | Builds image rootlessly, no Docker socket |
| Artifact Registry | Stores the image |
| Helm deploy | Rolls out the container |
| DB auto-wire | DATABASE_URL injected |
| SSL | Automatic 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.jsonAttach 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=Productionso detailed error pages are off. - Use
SSL Mode=Requireon 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