Introduction: Why Containerize C# Applications?
Modern C# (.NET) development rarely goes without Docker. Containerization allows you to package an application together with all its dependencies, runtime environment, and configurations into an isolated image. This solves the "it works on my machine" problem and simplifies deployment to a server, cloud, or local infrastructure.
In this article, we will break down how to create a Docker image for a .NET application (ASP.NET Core or console), optimize its size, use docker-compose to connect with a database, and configure CI/CD. All code examples are written in C# and ready to use.
1. Docker Basics for .NET Developers
Docker is a platform for running applications in containers. A container contains everything needed: code, runtime environment, libraries, and settings. This is especially convenient for .NET, as you don't need to install the .NET SDK on the server — Docker is sufficient.
1.1. Installing Docker
Download and install Docker Desktop from the official website. After installation, check the version:
docker --version1.2. .NET Project Structure
Suppose we have a simple ASP.NET Core Web API application. The folder structure:
MyApp/├── MyApp.csproj├── Program.cs├── Controllers/│ └── WeatherController.cs└── Dockerfile2. Creating a Dockerfile for a .NET Application
A Dockerfile is a set of instructions for building an image. For .NET, it is recommended to use multi-stage builds to separate the build and runtime stages. This reduces the final image size.
2.1. Basic Dockerfile
Create a Dockerfile file in the project root:
# Build stageFROM mcr.microsoft.com/dotnet/sdk:8.0 AS buildWORKDIR /srcCOPY ["MyApp.csproj", "."]RUN dotnet restoreCOPY . .RUN dotnet publish -c Release -o /app/publish
# Runtime stageFROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtimeWORKDIR /appCOPY --from=build /app/publish .EXPOSE 80ENTRYPOINT ["dotnet", "MyApp.dll"]Explanation:
- FROM ... AS build — uses the SDK for compilation.
- COPY ["MyApp.csproj", "."] — copies only the project file to cache the restore step.
- RUN dotnet restore — restores NuGet packages.
- RUN dotnet publish — builds the release version.
- FROM ... AS runtime — uses the minimal aspnet image.
- EXPOSE 80 — exposes port 80.
- ENTRYPOINT — the command to start the application.
2.2. Building and Running the Image
Run the following commands in the terminal from the project folder:
docker build -t myapp:latest .docker run -d -p 8080:80 --name myapp_container myapp:latestNow the application is available at http://localhost:8080.
3. Optimizing Docker Images for .NET
Large images slow down deployment and consume resources. Here are several practices for optimization:
3.1. Use .NET 8 (or newer)
Newer versions of .NET have built-in support for AOT (Ahead-of-Time) compilation, which reduces the runtime size. For ASP.NET Core, use the aspnet:8.0 image.
3.2. Minimize Layers
Each RUN or COPY instruction creates a layer. Combine commands:
RUN apt-get update && apt-get install -y \ curl \ && rm -rf /var/lib/apt/lists/*3.3. Use .dockerignore
Create a .dockerignore file in the project root to exclude unnecessary files:
bin/obj/.vs/*.user.git/README.md3.4. Example of an Optimized Dockerfile
FROM mcr.microsoft