Docker and C#: Advanced-Level Containerization of .NET Applications

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Docker and C#: Advanced-Level Application Containerization



Containerization of .NET applications using Docker has become an industry standard. If basic container skills have already been mastered, it's time to move to an advanced level. In this article, we will cover complex scenarios: multi-stage builds, creating lightweight production images, organizing microservices with Docker Compose, implementing health checks, and effective configuration management. All examples are relevant for .NET 8 and the latest versions of Docker.



1. Multi-stage Build for .NET



The classic problem is huge images that include the SDK and build tools. The solution is a multi-stage build, where the final image contains only the runtime and compiled binaries.



Let's look at an advanced Dockerfile for an ASP.NET Core Web API:



# Stage 1: Restore dependenciesFROM mcr.microsoft.com/dotnet/sdk:8.0 AS restoreWORKDIR /srcCOPY ["MyApp.Api/MyApp.Api.csproj", "MyApp.Api/"]COPY ["MyApp.Core/MyApp.Core.csproj", "MyApp.Core/"]RUN dotnet restore "MyApp.Api/MyApp.Api.csproj"

# Stage 2: Build and publishFROM restore AS buildCOPY . .WORKDIR "/src/MyApp.Api"RUN dotnet publish -c Release -o /app/publish

# Stage 3: Final image (runtime-only)FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS finalWORKDIR /appEXPOSE 8080ENV ASPNETCORE_URLS=http://+:8080COPY --from=build /app/publish .ENTRYPOINT ["dotnet", "MyApp.Api.dll"]


Key optimizations:

  • Separating restore and build: caching the dependencies layer speeds up rebuilds.
  • Target platform: use --no-restore at the publish stage if restore has already been performed.
  • Minimizing layers: combine RUN commands where possible using &&.
  • Using --chmod: for correct permissions in Linux containers.


2. Docker Compose for Microservices in C#



Docker Compose is indispensable when working with multiple .NET services. An advanced scenario includes configuring networks, dependencies, and environment variables.



Example docker-compose.yml for a microservice architecture with an API Gateway, an orders service, and RabbitMQ:



version: '3.8'

services: rabbitmq: image: rabbitmq:3-management-alpine ports: - "5672:5672" - "15672:15672" healthcheck: test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"] interval: 30s timeout: 10s retries: 5

orders-api: build: context: . dockerfile: src/Orders.Api/Dockerfile ports: - "5001:8080" environment: - ASPNETCORE_ENVIRONMENT=Production - ConnectionStrings__DefaultConnection=Server=db;Database=OrdersDb;User=sa;Password=Your_password123; - RabbitMQ__Host=rabbitmq depends_on: rabbitmq: condition: service_healthy db: condition: service_started restart: unless-stopped

api-gateway: build: context: . dockerfile: src/ApiGateway/Dockerfile ports: - "80:8080" depends_on: - orders-api environment: - ASPNETCORE_ENVIRONMENT=Production - Services__Orders=http://orders-api:8080


Advanced techniques:

  • Healthcheck in Compose: use condition: service_healthy for strict dependencies.
  • Profiles: enable/disable services for different development environments.
  • Built-in volumes for logs: volumes: to persist logs outside the container.
  • Environment variables via .env file: store sensitive

Blogs

Book Recommendations