ASP.NET Core is cross-platform, runs great in Linux containers, and ships with Kestrel — a fast, production-grade web server. The friction points when deploying are almost always the same: binding Kestrel to the right port, handling forwarded headers behind a proxy, and wiring health checks. This guide covers each.
A multi-stage publish
The official .NET images make a clean two-stage build. The SDK image compiles; the smaller ASP.NET runtime image serves.
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 /p:UseAppHost=false
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", "YourApp.dll"]The key line is ASPNETCORE_URLS=http://0.0.0.0:8080. By default Kestrel binds to localhost, which is unreachable from outside the container. Many platforms inject PORT; if so, read it:
ASPNETCORE_URLS=http://0.0.0.0:${PORT}Forwarded headers behind a proxy
In the cloud your app sits behind an ingress or load balancer that terminates TLS. Without forwarded-headers middleware, Request.Scheme reports http and redirects break, and Request.RemoteIpAddress shows the proxy, not the client.
var app = builder.Build();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});Put this early in the middleware pipeline, before authentication and HTTPS redirection.
Health checks
ASP.NET Core has built-in health checks. Register them and map endpoints:
builder.Services.AddHealthChecks()
.AddNpgSql(builder.Configuration.GetConnectionString("Default"));
app.MapHealthChecks("/healthz");
app.MapHealthChecks("/readyz", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});The AddNpgSql check (from AspNetCore.HealthChecks.Npgsql) verifies database connectivity so readiness reflects real dependencies.
Configuration and connection strings
ASP.NET Core's configuration system reads environment variables with a __ (double underscore) separator for nested keys. To set a connection string:
ConnectionStrings__Default=Host=host;Port=5432;Database=db;Username=u;Password=pIf your platform provides a DATABASE_URL instead, parse it at startup into a Npgsql-style connection string, since the two formats differ.
Don't run as root
The .NET 8 images support a non-root user out of the box:
USER $APP_UIDCombined with the read-only filesystem most platforms encourage, this hardens the container with almost no effort.
Deploying on PandaStack
- 1Commit the Dockerfile to your repo.
- 2Connect the repo as a container app in the [dashboard](https://dashboard.pandastack.io).
- 3PandaStack builds the image with rootless BuildKit in an ephemeral Kubernetes Job — no host Docker socket — and deploys via Helm with automatic SSL.
- 4Set
ASPNETCORE_URLSand connection-string variables under the app's environment settings. - 5Attach a managed PostgreSQL database and point your connection string at the injected credentials.
- 6Configure probes against
/healthzand/readyz.
Because PandaStack terminates TLS at Kong ingress, the forwarded-headers middleware above is what makes HTTPS-aware features behave correctly.
Deploy checklist
| Concern | Fix |
|---|---|
| Unreachable app | Bind 0.0.0.0, read PORT |
| Broken redirects/HTTPS | Forwarded headers middleware |
| Health probes | MapHealthChecks with DB check |
| Security | Non-root $APP_UID |
| Secrets | Platform env vars, not appsettings |
References
- [ASP.NET Core: Host and deploy in containers](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/docker/)
- [Configure ASP.NET Core to work with proxy servers](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer)
- [Health checks in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks)
- [.NET container images](https://learn.microsoft.com/en-us/dotnet/core/docker/introduction)
Get your ASP.NET Core app online with a managed database and free SSL on PandaStack's free tier — start at [dashboard.pandastack.io](https://dashboard.pandastack.io).