Breaking / LiveLIVE
DEV CommunityBear UI 1.3.5 — charts, AI, code, maps, and kanban now have their own packagesWIREDOil Spills Wreak Havoc on the Bottom of the Ocean, Even a Decade LaterHacker NewsFrance asked ISPs to prepare temporary DNS blocks for major social platformsGoogle DevelopersBring multimodal semantic search to the edge with EmbeddingGemma 2The Hacker NewsOpenAI AI Agents Demonstrate VM Escapes and Secret Cross-Sandbox CommunicationInfoQPingCAP Breach Exposes Customer Records and Source CodeThe VergeBlue Planet III is keeping the rough edges in the age of AITechCrunchStarlink seems to be working normally again in UkraineBleepingComputerBlackX UEFI bootkit exploits Baton Drop flaw to bypass Secure BootArs TechnicaA 1,000-mile cloud on Mars may reveal physics never seen in natureGitHub ChangelogUsage metrics API adds usage from tools like OpenCodeMicrosoft Power PlatformAI that understands your business: Dataverse and Work IQ at PPCC 2026AWS What's NewWeb Search on Amazon Bedrock now supports domain filtering and user location controlsMIT News: AIAt MIT, Martha Minow speaks on sustaining democracy, both by using and overcoming AIDEV CommunityBear UI 1.3.5 — charts, AI, code, maps, and kanban now have their own packagesWIREDOil Spills Wreak Havoc on the Bottom of the Ocean, Even a Decade LaterHacker NewsFrance asked ISPs to prepare temporary DNS blocks for major social platformsGoogle DevelopersBring multimodal semantic search to the edge with EmbeddingGemma 2The Hacker NewsOpenAI AI Agents Demonstrate VM Escapes and Secret Cross-Sandbox CommunicationInfoQPingCAP Breach Exposes Customer Records and Source CodeThe VergeBlue Planet III is keeping the rough edges in the age of AITechCrunchStarlink seems to be working normally again in UkraineBleepingComputerBlackX UEFI bootkit exploits Baton Drop flaw to bypass Secure BootArs TechnicaA 1,000-mile cloud on Mars may reveal physics never seen in natureGitHub ChangelogUsage metrics API adds usage from tools like OpenCodeMicrosoft Power PlatformAI that understands your business: Dataverse and Work IQ at PPCC 2026AWS What's NewWeb Search on Amazon Bedrock now supports domain filtering and user location controlsMIT News: AIAt MIT, Martha Minow speaks on sustaining democracy, both by using and overcoming AI

Docker for .NET Developers: From First Container to Production Image

A practical walkthrough of containerising an ASP.NET Core app: multi-stage builds, small secure images, configuration, health checks and common mistakes.

By KHR Editorial Team··3 min read
Docker for .NET Developers: From First Container to Production Image

Containers solve a familiar problem: "it works on my machine". With Docker, your ASP.NET Core application runs with the same runtime, dependencies and settings on a laptop, a test server and production. This guide walks through containerising a .NET web API properly, not just getting it to start.

The basic multi-stage Dockerfile

A multi-stage build compiles the app in an SDK image and copies only the published output into a smaller runtime image.

# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["Shop.Api/Shop.Api.csproj", "Shop.Api/"]
RUN dotnet restore "Shop.Api/Shop.Api.csproj"
COPY . .
RUN dotnet publish "Shop.Api/Shop.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
USER app
EXPOSE 8080
ENTRYPOINT ["dotnet", "Shop.Api.dll"]

Two details matter here:

  • Copying the .csproj and restoring before copying the rest of the source lets Docker cache the restore step. Builds become much faster when only code changes.
  • The final image contains the ASP.NET runtime, not the SDK, so it is much smaller and has fewer tools an attacker could use.

Run as a non-root user

Recent official .NET images include a non-root user named app, and listen on port 8080 by default. The USER app line ensures your process does not run as root inside the container. This is a simple but important security improvement.

Add a .dockerignore

Without it, Docker sends your bin, obj, .git and local secrets to the build context.

**/bin
**/obj
**/.vs
**/.git
**/*.user
**/appsettings.Development.json

Configuration and secrets

Containers should be configured from the environment, not by editing files inside the image.

  • ASP.NET Core reads environment variables automatically. ConnectionStrings__Default maps to ConnectionStrings:Default.
  • Never bake passwords or API keys into the image. Pass them at runtime through your orchestrator's secret store (Docker secrets, Kubernetes secrets, a cloud key vault).
  • Use the same image for every environment and change only the configuration.
docker run -p 8080:8080 \
  -e ASPNETCORE_ENVIRONMENT=Production \
  -e ConnectionStrings__Default="Server=db;Database=shop;..." \
  shop-api:1.4.0

Health checks

Orchestrators need to know whether your app is alive and ready.

builder.Services.AddHealthChecks()
    .AddDbContextCheck<ShopDb>();

app.MapHealthChecks("/health/live", new() { Predicate = _ => false });
app.MapHealthChecks("/health/ready");

The liveness endpoint only proves the process responds; the readiness endpoint also checks dependencies such as the database. Kubernetes and most cloud container services can call these endpoints.

Local development with Docker Compose

Compose lets you start the API and its database together:

services:
  api:
    build: .
    ports: ["8080:8080"]
    environment:
      ConnectionStrings__Default: "Server=db;Database=shop;User Id=sa;Password=${SA_PASSWORD};TrustServerCertificate=True"
    depends_on: [db]
  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      ACCEPT_EULA: "Y"
      MSSQL_SA_PASSWORD: "${SA_PASSWORD}"

Keep the password in a local .env file that is not committed to Git.

Making images smaller and safer

  • Use the runtime or aspnet image, not the SDK, for the final stage.
  • Consider the chiseled Ubuntu-based .NET images, which remove the shell and package manager for a smaller attack surface.
  • Rebuild images regularly to pick up security patches in the base image.
  • Scan images for known vulnerabilities in your CI pipeline.
  • Tag images with a version or commit hash, never deploy latest to production.

Common mistakes

Mistake Better approach
Copying the whole source before restore Copy project files, restore, then copy source
Running as root Use the app user
Secrets in Dockerfile or appsettings Environment variables and secret stores
One image per environment One image, different configuration
Writing files inside the container Use volumes or object storage
No health endpoints Separate liveness and readiness checks

Frequently asked questions

Do we need Docker on Windows servers? Not necessarily. ASP.NET Core runs well on IIS or as a Windows service. Docker becomes valuable when you deploy to Linux, Kubernetes or cloud container services, or want identical environments across machines.

Should the database also run in a container? For local development and testing, yes, it is very convenient. For production, most teams prefer a managed database or a dedicated server with proper backups and storage.

How do we see logs from a container? Write logs to standard output (the console). Docker and container platforms collect them, and you can forward them to a central logging system.

Key takeaways

  • Use multi-stage builds with cached restore and a small runtime image.
  • Run as non-root, add a .dockerignore and keep secrets out of images.
  • Configure through environment variables and use one image for all environments.
  • Add health checks and rebuild images regularly for security patches.

At KHR Digital Solutions we containerise .NET APIs and Next.js frontends for clients who need repeatable deployments. Once the Dockerfile is right, moving between servers and cloud providers becomes much simpler.

Docker for .NET Developers: From First Container to Production Image | KHR Tech Blog