Creating a containerized .NET core application in less than 10 lines of code

Hans Kilian
5 min readNov 21, 2018

One of the strengths of Docker is the ability to build on other peoples’ containers and create your own. Here I’ll show you how to create a Docker container image containing a .NET core application very quickly.

The only things we’ll need is a machine with Docker installed and an Internet connection.

Create a new directory and in that directory create a file called Dockerfile. In that file, paste the following lines:

FROM microsoft/dotnet:2.1-sdk AS build-env
WORKDIR /app
RUN dotnet new razor
RUN dotnet publish -c Release -o out
FROM microsoft/dotnet:2.1-aspnetcore-runtime-alpine
WORKDIR /app
COPY --from=build-env /app/out ./
ENTRYPOINT ["dotnet", "app.dll"]

and save it.

Run the command docker build -t myapp .

This creates a Docker image from the Dockerfile in the current directory and tags it with the name myapp.

We can then run the containerized app with the command docker run -d -p 80:80 myapp

Now, if you start a browser and point it to http://localhost/ you will see the following screen:

So how does it work?

The Dockerfile specifies a multi-stage build that consists of two parts, each 4 lines long. The first part generates the source code for the app and builds it. The second part packages the built app into a container image that we can run. Multi-stage builds were introduced with Docker 17.05 so you need that or a newer version to use this feature.

  • The first line specifies that we want to build on a Docker image made by Microsoft that contains the .NET core 2.1 SDK. We give this image the name build-env. We’ll use that later.
  • In that image we want to create a working directory called /app.
  • In that directory we then run the command dotnet new razor. That creates all the source files needed for a basic ASP.NET application using Razor pages.
  • Then we publish the app to a directory called out. The full path is /app/out since our working directory is /app. The publish command is smart enough to restore any needed Nuget packages and build the application before publishing it.

The last 4 lines in the Dockerfile package the published app into a Docker image that only contains the .NET core runtime. There’s no need for the complete SDK when we run the application. We want the runtime image to be as small as possible.

  • We start off by specifying that we want to start building on a different pre-made Docker image. This time we’ll use one that is built on the Alpine Linux distribution and contains the ASP.NET core runtime code. Alpine is a very small Linux distribution and is widely used as the base for Docker images.
  • Then we — again — create a working directory and we call it /app again. This is not the same as the first /app directory because we’re building on another Docker image now.
  • Then we copy the application files from the first image. The --from=build-env option specifies that we want to copy from the first image. We copy everything from /app/out in the first image and place it in the current directory, which is /app.
  • Then we specify that the entrypoint for the container is the dotnet command and it should be run with the parameter app.dll.

When we run the image, we specify a couple of parameters. One is -d which specifies that the image should be run in the background. The second is -p 80:80 which specifies that port 80 in the container should be mapped to port 80 on the machine the container is running on. If port 80 is taken by something else on your machine, you can map it to something else. If you want to map it to port 8080, then you would change the parameter to -p 8080:80. And then you would access the app by going to http://localhost:8080/. No matter what port on the machine you map it to, the app itself always acts like it’s using port 80.

You can see which images are running on your machine by running the command docker ps.

You can then stop the container with the command docker kill <name>.

Here are all the Docker commands we’ve used shown in sequence:

Where to go from here

Of course this is not a very useful example. To make it more useful, you can easily change it to build and containerize a custom C# project. If you have the source code in a sub-directory called code, you would replace RUN dotnet new razor with COPY ./code/ ./ and with that single-line change, it would now build your custom project. Or your could pull in a Git project with a git clone command.

Another nice thing about building your solution in a Docker container is that you don’t have to worry about having conflicting versions of SDKs installed on your machine. If you wanted to try out the .NET core 2.2 preview, you would simply ask for the 2.2 SDK and 2.2 ASP.NET runtime images like this:

FROM microsoft/dotnet:2.2.100-preview3-sdk AS build-env
WORKDIR /app
RUN dotnet new razor
RUN dotnet publish -c Release -o out
FROM microsoft/dotnet:2.2.0-preview3-aspnetcore-runtime-alpine
WORKDIR /app
COPY --from=build-env /app/out ./
ENTRYPOINT [“dotnet”, “app.dll”]

If you don’t like the results (and it doesn’t seem to work as well as 2.1 right now), you don’t have any SDK cleaning up to do because the 2.2 preview SDK was never installed on your machine. The 2.2 SDK is only installed inside a Docker image that is downloaded but not running unless you ask for it.

If you want to see what other .NET Docker images Microsoft make available, you can start here: https://hub.docker.com/r/microsoft/dotnet/

I hope this inspires you to try out Docker and/or .NET core if you haven’t already. Adding things like a PostgreSQL database is very easy since there are ready-built Docker images available that you can use without any customization at all. When you start having multiple containers that need to talk to each other, docker-compose is a great tool that can spin up and take down multiple containers with a single command.

--

--