This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can trysigning in orchanging directories.
Access to this page requires authorization. You can trychanging directories.
In this tutorial, you learn how to containerize a .NET application with Docker. Containers have many features and benefits, such as being an immutable infrastructure, providing a portable architecture, and enabling scalability. The image can be used to create containers for your local development environment, private cloud, or public cloud.
In this tutorial, you:
You explore the Docker container build and deploy tasks for a .NET application. TheDocker platform uses theDocker engine to quickly build and package apps asDocker images. These images are written in theDockerfile format to be deployed and run in a layered container.
Tip
If you're interested in publishing your .NET app as a container without the need for Docker or Podman, seeContainerize a .NET app with dotnet publish.
Note
This tutorialis not for ASP.NET Core apps. If you're using ASP.NET Core, see theLearn how to containerize an ASP.NET Core application tutorial.
Install the following prerequisites:
dotnet --info command to determine which SDK you're using.dotnet --info command to determine which SDK you're using.dotnet --info command to determine which SDK you're using.You need a .NET app that the Docker container runs. Open your terminal, create a working folder if you haven't already, and enter it. In the working folder, run the following command to create a new project in a subdirectory namedApp:
dotnet new console -o App -n DotNet.DockerYour folder tree looks similar to the following directory structure:
📁 docker-working └──📂 App ├──DotNet.Docker.csproj ├──Program.cs └──📂 obj ├── DotNet.Docker.csproj.nuget.dgspec.json ├── DotNet.Docker.csproj.nuget.g.props ├── DotNet.Docker.csproj.nuget.g.targets ├── project.assets.json └── project.nuget.cacheThedotnet new command creates a new folder namedApp and generates a "Hello World" console application. Now, you change directories and navigate into theApp folder from your terminal session. Use thedotnet run command to start the app. The application runs, and printsHello World! below the command:
cd Appdotnet runHello World!The default template creates an app that prints to the terminal and then immediately terminates. For this tutorial, you use an app that loops indefinitely. Open theProgram.cs file in a text editor.
Tip
If you're using Visual Studio Code, from the previous terminal session type the following command:
code .This command opens theApp folder that contains the project in Visual Studio Code.
TheProgram.cs should look like the following C# code:
Console.WriteLine("Hello World!");Replace the file with the following code that counts numbers every second:
var counter = 0;var max = args.Length is not 0 ? Convert.ToInt32(args[0]) : -1;while (max is -1 || counter < max){ Console.WriteLine($"Counter: {++counter}"); await Task.Delay(TimeSpan.FromMilliseconds(1_000));}Save the file and test the program again withdotnet run. Remember that this app runs indefinitely. Use the cancel commandCtrl+C to stop it. Consider the following example output:
dotnet runCounter: 1Counter: 2Counter: 3Counter: 4^CIf you pass a number on the command line to the app, it limits the count to that amount and then exits. Try it withdotnet run -- 5 to count to five.
Important
Any parameters after-- aren't passed to thedotnet run command and instead are passed to your application.
In order for the app to be suitable for an image creation it has to compile. Thedotnet publish command is most apt for this, as it builds and publishes the app. For an in-depth reference, seedotnet build anddotnet publish commands documentation.
dotnet publish -c ReleaseTip
If you're interested in publishing your .NET app as a container without the need for Docker, seeContainerize a .NET app with dotnet publish.
Thedotnet publish command compiles your app to thepublish folder. The path to thepublish folder from the working folder should be./App/bin/Release/<TFM>/publish/:
From theApp folder, get a directory listing of the publish folder to verify that theDotNet.Docker.dll file was created.
dir .\bin\Release\net9.0\publish\ Directory: C:\Users\default\docker-working\App\bin\Release\net9.0\publishMode LastWriteTime Length Name---- ------------- ------ -----a---- 1/6/2025 10:11 AM 431 DotNet.Docker.deps.json-a---- 1/6/2025 10:11 AM 6144 DotNet.Docker.dll-a---- 1/6/2025 10:11 AM 145408 DotNet.Docker.exe-a---- 1/6/2025 10:11 AM 11716 DotNet.Docker.pdb-a---- 1/6/2025 10:11 AM 340 DotNet.Docker.runtimeconfig.jsonTheDockerfile file is used by thedocker build command to create a container image. This file is a text file namedDockerfile that doesn't have an extension.
Create a file namedDockerfile in the directory containing the.csproj and open it in a text editor. This tutorial uses the ASP.NET Core runtime image (which contains the .NET runtime image) and corresponds with the .NET console application.
FROM mcr.microsoft.com/dotnet/sdk:9.0@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS buildWORKDIR /App# Copy everythingCOPY . ./# Restore as distinct layersRUN dotnet restore# Build and publish a releaseRUN dotnet publish -o out# Build runtime imageFROM mcr.microsoft.com/dotnet/aspnet:9.0@sha256:b4bea3a52a0a77317fa93c5bbdb076623f81e3e2f201078d89914da71318b5d8WORKDIR /AppCOPY --from=build /App/out .ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]Note
The ASP.NET Core runtime image is used intentionally here, although themcr.microsoft.com/dotnet/runtime:9.0 image could be used instead.
Important
Including a secure hash algorithm (SHA) after the image tag in aDockerfile is a best practice. This ensures that the image is not tampered with and that the image is the same as the one you expect. The SHA is a unique identifier for the image. For more information, seeDocker Docs: Pull an image by digest.
Tip
ThisDockerfile uses multi-stage builds, which optimize the final size of the image by layering the build and leaving only required artifacts. For more information, seeDocker Docs: multi-stage builds.
TheFROM keyword requires a fully qualified Docker container image name. The Microsoft Container Registry (MCR, mcr.microsoft.com) is a syndicate of Docker Hub, which hosts publicly accessible containers. Thedotnet segment is the container repository, whereas thesdk oraspnet segment is the container image name. The image is tagged with9.0, which is used for versioning. Thus,mcr.microsoft.com/dotnet/aspnet:9.0 is the .NET 9.0 runtime. Make sure that you pull the runtime version that matches the runtime targeted by your SDK. For example, the app created in the previous section used the .NET 9.0 SDK, and the base image referred to in theDockerfile is tagged with9.0.
Important
When using Windows-based container images, you need to specify the image tag beyond simply9.0, for example,mcr.microsoft.com/dotnet/aspnet:9.0-nanoserver-1809 instead ofmcr.microsoft.com/dotnet/aspnet:9.0. Select an image name based on whether you're using Nano Server or Windows Server Core and which version of that OS. You can find a full list of all supported tags on .NET'sDocker Hub page.
Save theDockerfile file. The directory structure of the working folder should look like the following. Some of the deeper-level files and folders are omitted to save space in the article:
📁 docker-working └──📂 App ├── Dockerfile ├── DotNet.Docker.csproj ├── Program.cs ├──📂 bin │ └───📂 Release │ └───📂 net9.0 │ ├───📂 publish │ │ ├─── DotNet.Docker.deps.json │ │ ├─── DotNet.Docker.dll │ │ ├─── DotNet.Docker.exe │ │ ├─── DotNet.Docker.pdb │ │ └─── DotNet.Docker.runtimeconfig.json │ ├─── DotNet.Docker.deps.json │ ├─── DotNet.Docker.dll │ ├─── DotNet.Docker.exe │ ├─── DotNet.Docker.pdb │ └─── DotNet.Docker.runtimeconfig.json └──📁 obj └──...TheENTRYPOINT instruction setsdotnet as the host for theDotNet.Docker.dll. However, it's possible to instead define theENTRYPOINT as the app executable itself, relying on the OS as the app host:
ENTRYPOINT ["./DotNet.Docker"]This causes the app to be executed directly, withoutdotnet, and instead relies on the app host and the underlying OS. For more information on publishing cross-platform binaries, seeCross-platform DLL deployment.
To build the container, from your terminal, run the following command:
docker build -t counter-image -f Dockerfile .Docker processes each line in theDockerfile. The. in thedocker build command sets the build context of the image. The-f switch is the path to theDockerfile. This command builds the image and creates a local repository namedcounter-image that points to that image. After this command finishes, rundocker images to see a list of images installed:
REPOSITORY TAG IMAGE ID CREATED SIZEcounter-image latest 1c1f1433e51d 32 seconds ago 223MBThecounter-image repository is the name of the image. Additionally, the image tag, image identifier, size and when it was created are all part of the output. The final steps of theDockerfile are to create a container from the image and run the app, copy the published app to the container, and define the entry point:
FROM mcr.microsoft.com/dotnet/aspnet:9.0WORKDIR /AppCOPY --from=build /App/out .ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]TheFROM command specifies the base image and tag to use. TheWORKDIR command changes thecurrent directory inside of the container toApp.
TheCOPY command tells Docker to copy the specified source directory to a destination folder. In this example, thepublish contents in thebuild layer are output into the folder namedApp/out, so it's the source to copy from. All of the published contents in theApp/out directory are copied into current working directory (App).
The next command,ENTRYPOINT, tells Docker to configure the container to run as an executable. When the container starts, theENTRYPOINT command runs. When this command ends, the container automatically stops.
Tip
Before .NET 8, containers configured to run as read-only might fail withFailed to create CoreCLR, HRESULT: 0x8007000E. To address this issue, specify aDOTNET_EnableDiagnostics environment variable as0 (just before theENTRYPOINT step):
ENV DOTNET_EnableDiagnostics=0For more information on various .NET environment variables, see.NET environment variables.
Now that you have an image that contains your app, you can create a container. You can create a container in two ways. First, create a new container that is stopped.
docker create --name core-counter counter-imageThisdocker create command creates a container based on thecounter-image image. The output of thedocker create command shows you theCONTAINER ID of the container (your identifier will be different):
d0be06126f7db6dd1cee369d911262a353c9b7fb4829a0c11b4b2eb7b2d429cfTo see a list ofall containers, use thedocker ps -a command:
docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESd0be06126f7d counter-image "dotnet DotNet.Docke…" 12 seconds ago Created core-counterThe container was created with a specific namecore-counter. This name is used to manage the container. The following example uses thedocker start command to start the container, and then uses thedocker ps command to only show containers that are running:
docker start core-countercore-counterdocker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMEScf01364df453 counter-image "dotnet DotNet.Docke…" 53 seconds ago Up 10 seconds core-counterSimilarly, thedocker stop command stops the container. The following example uses thedocker stop command to stop the container, and then uses thedocker ps command to show that no containers are running:
docker stop core-countercore-counterdocker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESAfter a container is running, you can connect to it to see the output. Use thedocker start anddocker attach commands to start the container and peek at the output stream. In this example, theCtrl+C keystroke is used to detach from the running container. This keystroke ends the process in the container unless otherwise specified, which would stop the container. The--sig-proxy=false parameter ensures thatCtrl+C doesn't stop the process in the container.
After you detach from the container, reattach to verify that it's still running and counting.
docker start core-countercore-counterdocker attach --sig-proxy=false core-counterCounter: 7Counter: 8Counter: 9^Cdocker attach --sig-proxy=false core-counterCounter: 17Counter: 18Counter: 19^CFor this article, you don't want containers hanging around that don't do anything. Delete the container you previously created. If the container is running, stop it.
docker stop core-counterThe following example lists all containers. It then uses thedocker rm command to delete the container and then checks a second time for any running containers.
docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES2f6424a7ddce counter-image "dotnet DotNet.Dock…" 7 minutes ago Exited (143) 20 seconds ago core-counterdocker rm core-countercore-counterdocker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESDocker provides thedocker run command to create and run the container as a single command. This command eliminates the need to rundocker create and thendocker start. You can also set this command to automatically delete the container when the container stops. For example, usedocker run -it --rm to do two things, first, automatically use the current terminal to connect to the container, and then when the container finishes, remove it:
docker run -it --rm counter-imageCounter: 1Counter: 2Counter: 3Counter: 4Counter: 5^CThe container also passes parameters into the execution of the .NET app. To instruct the .NET app to count only to three, pass in 3.
docker run -it --rm counter-image 3Counter: 1Counter: 2Counter: 3Withdocker run -it, theCtrl+C command stops the process that's running in the container, which in turn, stops the container. Since the--rm parameter was provided, the container is automatically deleted when the process is stopped. Verify that it doesn't exist:
docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESThedocker run command also lets you modify theENTRYPOINT command from theDockerfile and run something else, but only for that container. For example, use the following command to runbash orcmd.exe. Edit the command as necessary.
In this example,ENTRYPOINT is changed tocmd.exe.Ctrl+C is pressed to end the process and stop the container.
docker run -it --rm --entrypoint "cmd.exe" counter-imageMicrosoft Windows [Version 10.0.17763.379](c) 2018 Microsoft Corporation. All rights reserved.C:\>dir Volume in drive C has no label. Volume Serial Number is 3005-1E84 Directory of C:\04/09/2019 08:46 AM <DIR> app03/07/2019 10:25 AM 5,510 License.txt04/02/2019 01:35 PM <DIR> Program Files04/09/2019 01:06 PM <DIR> Users04/02/2019 01:35 PM <DIR> Windows 1 File(s) 5,510 bytes 4 Dir(s) 21,246,517,248 bytes freeC:\>^CNote
This example only works on Windows containers. Linux containers don't havecmd.exe.
Docker has many different commands that create, manage, and interact with containers and images. These Docker commands are essential to managing your containers:
During this tutorial, you created containers and images. If you want, delete these resources. Use the following commands to
List all containers
docker ps -aStop containers that are running by their name.
docker stop core-counterDelete the container
docker rm core-counterNext, delete any images that you no longer want on your machine. Delete the image created by yourDockerfile and then delete the .NET image theDockerfile was based on. You can use theIMAGE ID or theREPOSITORY:TAG formatted string.
docker rmi counter-image:latestdocker rmi mcr.microsoft.com/dotnet/aspnet:10.0docker rmi counter-image:latestdocker rmi mcr.microsoft.com/dotnet/aspnet:9.0docker rmi counter-image:latestdocker rmi mcr.microsoft.com/dotnet/aspnet:8.0Use thedocker images command to see a list of images installed.
Tip
Image files can be large. Typically, you would remove temporary containers you created while testing and developing your app. You usually keep the base images with the runtime installed if you plan on building other images based on that runtime.
Was this page helpful?
Need help with this topic?
Want to try using Ask Learn to clarify or guide you through this topic?
Was this page helpful?
Want to try using Ask Learn to clarify or guide you through this topic?