ZetCode

C# Docker

last modified October 15, 2026

In this article we show how to use Docker for C# applications.

Application containerization is an OS-level virtualization method used to deploy and run distributed applications without having to create an entire virtual machine for each application. With containers, it is possible to run multiple isolated applications or services on a single host and access the same OS kernel. Containers work on bare-metal systems, cloud instances or virtual machines.

Docker

Docker is a platform for developers and sysadmins to build, run, and share applications with containers. Docker improves application portability and scalability. It provides application isolation and thus eliminates many issues caused by library and environment differences. It helps automate development and deployment. With predefined community images, developers save time and improve their overall experience.

A Docker image is a read-only template with instructions for creating a Docker container. A Docker container is a runnable instance of an image.

Images are stored in repositories. The Docker Hub is the official Docker repository. Docker Engine is the underlying client-server technology that builds and runs containers using Docker's components and services.

Dockerfile is a special file which contains instructions needed to build a Docker image.

$ docker --version
Docker version 29.1.3, build 29.1.3-0ubuntu3~22.04.2

This is the Docker version we use.

Microsoft provides official .NET images in the Microsoft Artifact Registry (mcr.microsoft.com). The sdk image contains the whole .NET SDK and is used to build applications. The runtime image contains only the .NET runtime and is used to run console applications. The aspnet image contains the ASP.NET Core runtime and is used to run web applications.

C# Docker console example

In the next example, we create and run a very simple Docker image. When we run the image, a console C# application is executed.

$ dotnet new console -o First

We create a new C# application.

Program.cs
Console.WriteLine("C# in Docker");

Console.WriteLine(Environment.OSVersion);
Console.WriteLine(Environment.Version);
Console.WriteLine(Environment.MachineName);

The application is a console program. It prints a message and some environment details.

Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

COPY *.csproj .
RUN dotnet restore

COPY . .
RUN dotnet publish -c Release -o /app --no-restore

FROM mcr.microsoft.com/dotnet/runtime:10.0
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "First.dll"]

A Dockerfile is a series of instructions to build the image. We use a multi-stage build. In the first stage, we build the application with the SDK image. In the second stage, we copy the compiled application into a much smaller runtime image.

FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build

The first stage is based on the official .NET 10 SDK image. We name the stage build so that we can refer to it later.

WORKDIR /src

The WORKDIR instruction sets the working directory for all subsequent instructions. The directory is created if it does not exist.

COPY *.csproj .
RUN dotnet restore

We copy the project file and restore the NuGet dependencies of the project. Docker caches each layer; since the dependencies are restored before the source code is copied, they do not have to be downloaded again when we only change the source code.

COPY . .
RUN dotnet publish -c Release -o /app --no-restore

We copy the rest of the files. The dotnet publish command compiles the application in the Release configuration and publishes the resulting set of files to the /app directory.

FROM mcr.microsoft.com/dotnet/runtime:10.0
WORKDIR /app
COPY --from=build /app .

The second stage starts from the runtime image. We copy the published files from the build stage. The SDK is not part of the final image.

ENTRYPOINT ["dotnet", "First.dll"]

The ENTRYPOINT instruction runs the program upon starting the container.

.dockerignore
bin/
obj/

The .dockerignore file excludes the local build artifacts from the files that are sent to Docker.

$ docker build -t first .

We build the image and call it first.

$ docker image ls first
IMAGE          ID             DISK USAGE   CONTENT SIZE   EXTRA
first:latest   97af7d72604e        203MB             0B

We list the image with the docker image ls command.

$ docker run --rm first
C# in Docker
Unix 6.8.0.138
10.0.13
70791844c521

We run the image. The --rm option removes the container when it exits. The machine name is the ID of the container.

$ dotnet run
C# in Docker
Unix 6.8.0.138
10.0.7
universum

This is the output of the program on our local machine. The container shares the kernel with the host; therefore, the OS version is the same.

C# Docker ASP.NET example

In the following example, we run an ASP.NET Core application inside a Docker container.

$ dotnet new web --no-https -o WebApp

We create a new web application.

Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "home page\n");
app.MapGet("/about", () => Results.Content("about page\n"));
app.MapGet("/contact", () => "contact page\n");

app.Run();

The application uses Minimal API; it has three endpoints.

Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src

COPY *.csproj .
RUN dotnet restore

COPY . .
RUN dotnet publish -c Release -o /app --no-restore

FROM mcr.microsoft.com/dotnet/aspnet:10.0
WORKDIR /app
COPY --from=build /app .

USER $APP_UID

EXPOSE 8080
ENTRYPOINT ["dotnet", "WebApp.dll"]

The Dockerfile contains instructions to build the image. For the final stage, we use the aspnet image.

USER $APP_UID

The official .NET images contain a non-root user called app; the APP_UID environment variable holds its user ID. Running the application as a non-root user is a recommended security practice.

EXPOSE 8080

The aspnet images configure the application to listen on port 8080 via the ASPNETCORE_HTTP_PORTS environment variable. The EXPOSE instruction documents this port.

$ docker build -t webapp .

We build the image.

$ docker run --rm -p 3000:8080 webapp
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://[::]:8080
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
...

We run the image. The container's 8080 port is mapped to our host's 3000 port.

$ curl localhost:3000
home page
$ curl localhost:3000/about
about page

We test the application with the curl tool.

Building images without Dockerfile

The .NET SDK can build container images directly, without a Dockerfile. The SDK chooses the appropriate base image for the project.

$ dotnet publish -c Release /t:PublishContainer
...
  Building image 'first' with tags 'latest' on top of base image 'mcr.microsoft.com/dotnet/runtime:10.0'.
  Pushed image 'first:latest' to local registry via 'docker'.

We build the image for the console application from the first example. The image name is derived from the project name. It can be changed with the ContainerRepository property in the project file.

$ docker run --rm first
C# in Docker
Unix 6.8.0.138
10.0.13
3905f906b951

We run the image.

Source

Containerize a .NET app

Containerize a .NET app with dotnet publish

In this article we have worked with C# and Docker.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.