How to containerize a .NET Core application
This tutorial teaches the Docker container build and deploy tasks for a .NET Core application. The Docker platform uses the Docker Engine to quickly build and package apps as Docker images. These images are written in the Dockerfile format to be deployed and run in a layered container.
During the course of this tutorial, you learn:
- How to create a Dockerfile
- How to create a .NET Core app.
- How to deploy your app into a Docker container.
.NET Core: Easiest way to get started
Before creating the Docker image, you need an application to containerize. You can create it on Linux, MacOS, or Windows. The quickest and easiest way to do that is to use .NET Core.
If you're unfamiliar with the .NET Core CLI toolset, read the .NET Core SDK overview.
You can build both Windows and Linux containers with multi-arch based tags.
Your first .NET Core Docker app
To complete this tutorial:
.NET Core SDK
- Install .NET Core 2.1 SDK or later.
See .NET Core 2.1 Supported OS Versions for the complete list of .NET Core 2.1 supported operating systems, out of support OS versions, and lifecycle policy links.
- Install your favorite code editor, if you haven't already.
Need to install a code editor? Try Visual Studio Code!
Installing Docker Client
Install Docker 18.06 or later of the Docker client.
The Docker client can be installed in:
Create a .NET Core 2.1 console app for Dockerization
Open a command prompt and create a folder named Hello. Navigate to the folder you created and type the following commands:
dotnet new console dotnet run
Let's do a quick walkthrough:
$ dotnet new console
dotnet newcreates an up-to-date
Hello.csprojproject file with the dependencies necessary to build a console app. It also creates a
Program.cs, a basic file containing the entry point for the application.
The project file specifies everything that's needed to restore dependencies and build the program.
OutputTypetag specifies that we're building an executable, in other words a console application.
TargetFrameworktag specifies what .NET implementation we're targeting. In an advanced scenario, you can specify multiple target frameworks and build to the specified frameworks in a single operation. In this tutorial, we build for .NET Core 2.1.
The program starts by
using System. This statement means, "Bring everything in the
Systemnamespace into scope for this file." The
Systemnamespace includes basic constructs such as
string, or numeric types.
We then define a namespace called
Hello. You can change namespace to anything you want. A class named
Programis defined within that namespace, with a
Mainmethod that takes an array of strings as its argument. This array contains the list of arguments passed in when the compiled program is called. In our example, the program only writes "Hello World!" to the console.
- analyzes the Hello.csproj file.
- downloads the file dependencies (or grabs from your machine cache).
- writes the obj/project.assets.json file.
The project.assets.json file is a complete set of the NuGet dependencies graph, binding resolutions, and other app metadata. This required file is used by other tools, such as
dotnet run, to correctly process the source code.
$ dotnet run
$ dotnet run Hello World!
For advanced scenarios, see .NET Core Application Deployment for details.
Dockerize the .NET Core application
The Hello .NET Core console app successfully runs locally. Now let's take it a step further and build and run the app in Docker.
Your first Dockerfile
Open your text editor and let's get started! We're still working from the Hello directory we built the app in.
Add the following Docker instructions for either Linux or Windows Containers to a new file. When finished, save it in the root of your Hello directory as Dockerfile, with no extension (You may need to set your file type to
All types (*.*) or something similar).
FROM microsoft/dotnet:2.1-sdk WORKDIR /app # copy csproj and restore as distinct layers COPY *.csproj ./ RUN dotnet restore # copy and build everything else COPY . ./ RUN dotnet publish -c Release -o out ENTRYPOINT ["dotnet", "out/Hello.dll"]
The Dockerfile contains Docker build instructions that run sequentially.
The first instruction must be FROM. This instruction initializes a new build stage and sets the Base Image for the remaining instructions. The multi-arch tags pull either Windows or Linux containers depending on the Docker for Windows container mode. The Base Image for our sample is the 2.1-sdk image from the microsoft/dotnet repository,
The WORKDIR instruction sets the working directory for any remaining RUN, CMD, ENTRYPOINT, COPY, and ADD Dockerfile instructions. If the directory doesn't exist, it's created. In this case, WORKDIR is set to the app directory.
The COPY instruction copies new files or directories from the source path and adds them to the destination container filesystem. With this instruction, we are copying the C# project file to the container.
COPY *.csproj ./
The RUN instruction executes any commands in a new layer on top of the current image and commit the results. The resulting committed image is used for the next step in the Dockerfile. We are running dotnet restore to get the needed dependencies of the C# project file.
RUN dotnet restore
This COPY instruction copies the rest of the files into our container into new layers.
COPY . ./
We are publishing the app with this RUN instruction. The dotnet publish command compiles the application, reads through its dependencies specified in the project file, and publishes the resulting set of files to a directory. Our app is published with a Release configuration and output to the default directory.
RUN dotnet publish -c Release -o out
The ENTRYPOINT instruction allows the container to run as an executable.
ENTRYPOINT ["dotnet", "out/Hello.dll"]
Now you have a Dockerfile that:
- copies your app to the image
- your app's dependencies to the image
- builds the app to run as an executable
Build and run the Hello .NET Core app
Essential Docker commands
These Docker commands are essential:
Build and run
You wrote the dockerfile; now Docker builds your app and then runs the container.
docker build -t dotnetapp-dev . docker run --rm dotnetapp-dev Hello from Docker
The output from the
docker build command should be similar to the following console output:
Sending build context to Docker daemon 173.1kB Step 1/7 : FROM microsoft/dotnet:2.1-sdk ---> 288f8c45f7c2 Step 2/7 : WORKDIR /app ---> Using cache ---> 9af1fbdc7972 Step 3/7 : COPY *.csproj ./ ---> Using cache ---> 86c8c332d4b3 Step 4/7 : RUN dotnet restore ---> Using cache ---> 86fcd7dd0ea4 Step 5/7 : COPY . ./ ---> Using cache ---> 6faf0a53607f Step 6/7 : RUN dotnet publish -c Release -o out ---> Using cache ---> f972328318c8 Step 7/7 : ENTRYPOINT dotnet out/Hello.dll ---> Using cache ---> 53c337887e18 Successfully built 46db075bd98d Successfully tagged dotnetapp-dev:latest
As you can see from the output, the Docker Engine used the Dockerfile to build our container.
The output from the
docker run command should be similar to the following console output:
Congratulations! You have just:
- Created a local .NET Core app
- Created a Dockerfile to build your first container
- Built and ran your Dockerized app
Here are some next steps you can take:
- Introduction to .NET Docker Images Video
- Visual Studio, Docker & Azure Container Instances better together!
- Docker for Azure Quickstarts
- Deploy your app on Docker for Azure
If you do not have an Azure subscription, sign up today for a free 30-day account and get $200 in Azure Credits to try out any combination of Azure services.
Docker Images used in this sample
The following Docker images are used in this sample