As a .NET developer, if you want to containerise your .NET apps, you know you need a Dockerfile first. Dockerfile is a spec that describes which OS is used, where the .NET code is located, how the .NET code is compiled and how the .NET app is executed. Once your Dockerfile is ready, you can build your container image. But is the Dockerfile the only option for building your .NET apps in a container? How about the container orchestration? Is there any way to orchestrate containers other than running the docker compose up command? Throughout this post, I’m going to discuss which options are available for .NET developers to containerise your .NET apps.
You can find a sample code from:
devkimchi
/
msbuild-for-containers
This provides sample .NET apps using container images with Dockerfile, with MSBuild, and with .NET Aspire support on Docker Desktop.
MSBuild for Containers
As a .NET developer, when you build a container image for your app, you can use the Dockerfile to define the container image. However, you can also use the dotnet publish command to build and publish the container image without a Dockerfile. This repository provides sample .NET apps using container images with Dockerfile and with dotnet publish.
In addition to that, if you want to orchestrate containers Docker Compose is usually the first approach. However, you can also use the .NET Aspire to generate the Docker Compose file from the .NET Aspire manifest JSON file. This repository also provides a sample .NET app using the .NET Aspire to orchestrate containers.
Prerequisites
.NET SDK 8.0+ with .NET Aspire workload
Visual Studio or Visual Studio Code + C# Dev Kit
Getting Started
Run with Dockerfile
Run docker init to create a new Dockerfile for…
Prerequisites
There are a few prerequisites to containerise .NET apps effectively.
.NET SDK 8.0+ with .NET Aspire workload and Aspirate
Containerise with Dockerfile
In the sample code repository, there are two .NET apps, ApiApp and WebApp. When you run both apps locally, it communicates with each other by running the following commands:
Now, you want to containerise both apps. How can you do that? The first option is to write a Dockerfile for each app. You can either manually write Dockerfile or run the command, docker init. Once you have Dockerfile files for both apps, run the following commands to build the container images:
docker build . -t apiapp:latest# For WebApp
docker build . -t webapp:latest
We all know how Dockerfile is useful to build container images. However, Dockerfile is yet another code and should be maintained. If you have many apps to containerise, you need to write Dockerfile files respectively. This is a bit cumbersome. Is there any other way to containerise .NET apps, without writing Dockerfile files?
Containerise with dotnet publish
MSBuild supports containerisation by itself. It uses the dotnet publish command to build the container image for each app. To do this, you might need to update your .csproj file to include the containerisation settings. The following is the MSBuildForContainers.ApiApp.csproj file:
<ContainerRepository>apiapp</ContainerRepository>
<ContainerImageTag>latest</ContainerImageTag>
</PropertyGroup>
These two properties replaces the –tag option in the docker build command. Once you have these properties in the .csproj file, run the following command. Note that the -t:PublishContainer indicates the containerisation and the –os and –arch options specify the target OS and architecture.
-t:PublishContainer
–os linux –arch x64
Then, you’ll have the container image. If you want to change the base image to the chiseled container one, add another property to the .csproj file. The following property sets the chiseled Ubuntu 24.04 image as the base image.
<ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:8.0-noble-chiseled</ContainerBaseImage>
</PropertyGroup>
Then, run the dotnet publish command above again. This time, the container image is built based on the chiseled image. Let’s do the same thing against the MSBuildForContainers.WebApp.csproj file. This way, you can containerise your .NET apps without writing Dockerfile files.
Alternatively, you don’t even need those properties in the .csproj file. You can run the following command to build the container image directly like this:
-t:PublishContainer
–os linux –arch x64
-p:ContainerBaseImage=mcr.microsoft.com/dotnet/aspnet:8.0-noble-chiseled
-p:ContainerRepository=apiapp
-p:ContainerImageTag=latest
You have the same development experience as building the container image with the docker build command, without having to rely on Dockerfile.
Now, you’ve got chiseled container images for both API app and web app. How to let them talk to each other?
Container orchestration with docker compose
The existing web app container image can’t talk to the API app container image because it doesn’t know where the API app container is. To make the web app container talk to the API app container, you need to slightly modify the web app and build the container image again. Open the MSBuildForContainers.WebApp/Program.cs file and update the base address value as follows:
builder.Services.AddHttpClient<IApiAppClient, ApiAppClient>(http => http.BaseAddress = new Uri(“https://localhost:5051/”));
// After
builder.Services.AddHttpClient<IApiAppClient, ApiAppClient>(http => http.BaseAddress = new Uri(“http://apiapp:8080/”));
Run the dotnet publish command again to build the web app container image. Now, you can manually run the docker run command to run both containers by attaching the same network. But for the container orchestration, the docker compose up command is much easier to use. The docker-compose.yml file is already prepared in the sample code repository. Run the following command:
Then, both containers are up and running. The web app container can talk to the API app container. This is how you can orchestrate containers with docker compose up. Again, this Docker Compose file is yet another code and should be maintained. If you have many apps to orchestrate, how would you manage them?
Container orchestration with .NET Aspire and Aspirate
.NET Aspire is to orchestrate .NET apps in containers. For this Docker Compose orchestration purpose, Aspirate is used, which is a tool that generates the Docker Compose file, a Kustomize file or helm file for the container orchestration. To use Aspirate, you need to install it first:
In the sample code repository, Both web app and API app are orchestrated with .NET Aspire in another branch. Switch to the aspire branch with the command:
Let’s generate the Docker Compose file with Aspirate. Run the following command to generate the .NET Aspire manifest file first:
—
–publisher manifest
–output-path ../aspire-manifest.json
Then, generate the Docker Compose file with the Aspirate command. Note that it intentionally excludes the .NET Aspire dashboard.
–project-path ./MSBuildForContainers.AppHost
–aspire-manifest ./aspire-manifest.json
–output-format compose
–disable-secrets –include-dashboard false
Now, you have the Docker Compose file generated. Run the docker compose up command to orchestrate the containers. This way, you can orchestrate your .NET apps in containers without manually writing the Docker Compose file.
So far, I’ve walked through how .NET developers can containerise their .NET apps with different options. You can either write Dockerfile files or use dotnet publish to build container images. You can orchestrate containers by writing the Docker Compose file by hand or letting .NET Aspire and Aspirate generate it automatically. Which one do you prefer?
More about MSBuild for Containers?
If you want to learn more options about containers with MSBuild, the following links might be helpful.
.NET Aspire
Containerise a .NET app with dotnet publish
.NET container images