.NET Academy

Docker for .NET

Package a .NET app into a container image, run it locally, and orchestrate it with its database using Docker Compose.

13 min read

You will learn

  • Explain images, containers and registries
  • Write a multi-stage Dockerfile for an ASP.NET Core app
  • Run an app and its dependencies with docker compose
  • Know about .NET's built-in container publishing

Images and containers

You met the idea in Track 7: a container image bundles your app with its runtime and dependencies so it runs identically on every machine. The vocabulary:

TermMeaning
ImageA read-only, layered template: OS base + .NET runtime + your published app
ContainerA running instance of an image, isolated from others
DockerfileThe recipe for building an image
RegistryWhere images are stored and shared: Docker Hub, GitHub Container Registry, Azure Container Registry

Install Docker Desktop (or Podman) to build and run containers locally.

A multi-stage Dockerfile

# ---- build stage: full SDK ----
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          # cached unless the .csproj changes
COPY . .
RUN dotnet publish Shop.Api/Shop.Api.csproj -c Release -o /app/publish
 
# ---- runtime stage: small ASP.NET Core runtime image ----
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
USER app                                             # don't run as root
EXPOSE 8080
ENTRYPOINT ["dotnet", "Shop.Api.dll"]
  • The build stage uses the large SDK image; only the published output is copied into the small runtime image.
  • Copying the .csproj and restoring first lets Docker cache the restore layer between builds.
  • Add a .dockerignore (bin/, obj/, .git/) so they don't bloat the build context.
  • .NET 8 images listen on port 8080 by default and include a non-root app user.
docker build -t shop-api .
docker run -p 8080:8080 -e ConnectionStrings__Shop="Host=..." shop-api

Configuration comes in through environment variables — the same Section__Key convention as before.

No Dockerfile needed

The .NET SDK can build container images directly:

dotnet publish --os linux --arch x64 /t:PublishContainer -p ContainerImageName=shop-api

Docker Compose: your local environment

Your API needs PostgreSQL and Redis. Compose starts them all together:

# docker-compose.yml
services:
  api:
    build: .
    ports: ["8080:8080"]
    environment:
      ConnectionStrings__Shop: "Host=db;Database=shop;Username=shop;Password=dev-only-password"
      ConnectionStrings__Redis: "cache:6379"
    depends_on: [db, cache]
 
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: shop
      POSTGRES_PASSWORD: dev-only-password
      POSTGRES_DB: shop
    volumes: ["pgdata:/var/lib/postgresql/data"]   # survives container restarts
 
  cache:
    image: redis:7
 
volumes:
  pgdata:
docker compose up -d       # start everything in the background
docker compose logs -f api
docker compose down        # stop and remove the containers (the volume keeps the data)

Services reach each other by name (db, cache) on Compose's private network.

.NET Aspire

For local development of multi-service .NET apps, .NET Aspire orchestrates projects and containers (databases, Redis, queues) from C#, wires up connection strings, and gives you a dashboard with logs, metrics and traces.

Containers in production

Containers are disposable: write persistent data to databases, object storage or volumes — never only to the container's filesystem. In production, an orchestrator (Kubernetes, Azure Container Apps, AWS ECS) pulls your image from a registry, runs replicas, checks health endpoints and rolls out new versions.

Key takeaways

  • An image is a template; a container is a running instance; registries store images.
  • Use multi-stage Dockerfiles: build with the SDK image, run on the slim runtime image as a non-root user.
  • Pass configuration through environment variables; keep data in volumes or external services.
  • Docker Compose (or .NET Aspire) runs your app with its dependencies locally.

Quiz

4 questions · 3 correct to pass
  1. 1.What's the difference between an image and a container?

  2. 2.Why use a multi-stage Dockerfile for .NET?

  3. 3.What does docker compose help with?

  4. 4.Where should a container keep data that must survive restarts, like a database's files?

To complete this lesson

  • ○ Pass the quiz (3/4 correct)