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

Prerequisites

To complete this tutorial:

.NET Core SDK

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.

Tip

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:

  1. $ dotnet new console

    dotnet new creates an up-to-date Hello.csproj project 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.

    Hello.csproj:

    The project file specifies everything that's needed to restore dependencies and build the program.

    • The OutputType tag specifies that we're building an executable, in other words a console application.
    • The TargetFramework tag 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.

    Program.cs:

    The program starts by using System. This statement means, "Bring everything in the System namespace into scope for this file." The System namespace 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 Program is defined within that namespace, with a Main method 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.

    dotnet new runs the dotnet restore command. Dotnet restore restores the tree of dependencies with a NuGet(.NET package manager) call. NuGet performs the following tasks:

    • 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 build and dotnet run, to correctly process the source code.

  2. $ dotnet run

    dotnet run calls dotnet build to confirm a successful build, and then calls dotnet <assembly.dll> to run the application.

    $ 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,

FROM microsoft/dotnet:2.1-sdk

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.

WORKDIR /app

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:

Hello World!

Congratulations! You have just:

  • Created a local .NET Core app
  • Created a Dockerfile to build your first container
  • Built and ran your Dockerized app

Next steps

Here are some next steps you can take:

Note

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