Docker and containerization is all the rage these days. Adoption in the .NET community has been slow so far, but that’s changing. With the introduction of cross-platform .NET Core, it became much easier to run .NET code on Linux machines. And, since Docker is a primarily Linux-based technology, it’s now very straightforward to deploy ASP.NET Core applications using Docker. How? Let’s take a look.

At a high level, containerization solves problems related to deploying and running your application on a server somewhere “out there.” I have fond memories of deploying code for the first time as a junior developer by manually copy-pasting binaries into production. Fortunately, no one is doing that anymore (right?). Today, automated build tools can handle the steps required to push code to a production machine. Containerization can take you one step further… by abstracting away the machine itself.

How does Docker work?

Docker is similar to a virtual machine, but without the overhead. A service called Docker Engine runs on your server, ready to host one (or many) containers. Containers that run on top of Docker Engine are completely isolated. As far as the applications inside each container are concerned, they’re running on separate machines. In reality, they’re just isolated processes on the same machine.

Containers run Docker images, which are packages that represent everything needed to spin up your application or service inside the container: dependencies, shared libraries, application binaries, and so on. During development, you define the “recipe” that is built into the final image. This recipe is called a Dockerfile.

Why use Docker instead of a virtual machine?

If you’ve ever set up a web or application server, think of everything necessary to go from a “bare metal” virtual machine to a running production server: besides installing the OS, you have to get the latest patches, install framework runtimes and dependencies, grab third-party libraries, configure networking and routes to your other services, install your application code, and then configure it all. And, if you have multiple servers in your environment, or you’re frequently spinning up new servers, you have to do this a lot.

Containerization allows you to do all of the setup work once, and build the result into an image you can immediately run on any machine using Docker Engine. It moves the focus away from setting up servers, and lets you instead focus on building images that include everything needed to run anywhere.

Containerization doesn’t replace virtual machines – in fact, the two technologies go hand-in-hand. A common approach is to have a handful of virtual machines in a cluster, each running Docker Engine and hosting many containers.

Ultimately, there are use cases for both technologies. You probably don’t need to boot up an entire guest OS and tens or hundreds of background services just to run a single application – running inside a thin container makes more sense. Conversely, sometimes you do want to have an entire OS dedicated to a particular task, especially if it’s something CPU-intensive. Virtual machines are well-suited for the latter, and containerization makes the former easier to manage.

If you’ve never set up Docker or deployed an ASP.NET Core project as a Docker image, this is the tutorial for you! I’ll walk through the steps required to:

Set up Docker on your machine

Create a sample ASP.NET Core project

Build a Docker image from your project

Run and test the image in a live container

This tutorial doesn’t require any existing knowledge of Docker and should take about 20 or 30 minutes to complete. Ready? Let’s go!

Setting up Docker

It’s easy to get started with Docker. First, you have to install the Docker Engine on your machine (or your server). Follow the official instructions for Windows 10, Mac, or Linux.

For my own testing, I installed Docker for Windows on my Windows 10 development environment, and also on my Mac. To make sure the service is installed correctly, run this from the terminal:

> docker --version Docker version 1.12.0-rc4, build e4a0dbc, experimental 1 2 3 > docker -- version Docker version 1.12.0 - rc4 , build e4a0dbc , experimental

If you see a version number, everything is working!

Creating an ASP.NET Core project

If you don’t already have the latest .NET Core tooling installed, grab that at the official .NET Core site. Once it’s installed on your machine, you can create a new directory and scaffold a new ASP.NET Core project easily:

mkdir AspNetCoreHelloWorld cd AspNetCoreHelloWorld dotnet new -t web 1 2 3 4 mkdir AspNetCoreHelloWorld cd AspNetCoreHelloWorld dotnet new - t web

To make sure everything is working, try running the project locally first:

dotnet restore dotnet run 1 2 3 dotnet restore dotnet run

The project should start listening on http://localhost:5000. Try it out in your browser!

Building a Dockerfile for ASP.NET Core

Docker needs a Dockerfile that contains the “recipe” for creating an image based on the project. Create a new file called Dockerfile in the project directory:

touch Dockerfile 1 2 touch Dockerfile

On Windows, you can run notepad Dockerfile instead, or use your favorite text editor. Make sure that this file isn’t saved with an extension like .txt – it’s supposed to have no extension.

The Dockerfile contents are straightforward:

FROM microsoft/dotnet:latest COPY . /app WORKDIR /app RUN ["dotnet", "restore"] RUN ["dotnet", "build"] EXPOSE 5000/tcp ENV ASPNETCORE_URLS http://*:5000 ENTRYPOINT ["dotnet", "run"] 1 2 3 4 5 6 7 8 9 10 11 12 FROM microsoft / dotnet : latest COPY . / app WORKDIR / app RUN [ "dotnet" , "restore" ] RUN [ "dotnet" , "build" ] EXPOSE 5000 / tcp ENV ASPNETCORE_URLS http : //*:5000 ENTRYPOINT [ "dotnet" , "run" ]

Here’s what each of these instructions does:

FROM tells Docker that you want to base your image on the existing image called microsoft/dotnet:latest . This image already contains all the dependencies for running .NET Core on Linux, so you don’t have to worry about setting those up.

COPY and WORKDIR copy the current directory’s contents into a new directory inside the container called /app , and set that to the the working directory for the subsequent instructions.

RUN executes dotnet restore and dotnet build , which restores the packages needed to run the ASP.NET Core application and compiles the project.

EXPOSE tells Docker to expose port 5000 (the default port for ASP.NET) on the container.

ENV sets the environment variable ASPNETCORE_URLS in the container. This will ensure that ASP.NET Core binds to the correct port and address.

ENTRYPOINT specifies the command to execute when the container starts up. In this case, it’s dotnet run .

Creating the Docker image

Once you’ve created the Dockerfile, you’re ready to build an image:

docker build -t mydemos:aspnetcorehelloworld . 1 2 docker build - t mydemos : aspnetcorehelloworld .

See that trailing period? That tells docker to look in the current directory for a Dockerfile. The -t flag tags the image with tag mydemos:aspnetcorehelloworld .

When the image finishes building, you can spin it up:

docker run -d -p 8080:5000 -t mydemos:aspnetcorehelloworld 1 2 docker run - d - p 8080 : 5000 - t mydemos : aspnetcorehelloworld

The -d flag tells Docker to run the container in detached mode (in the background). The -p flag will map port 8080 on the host machine to port 5000 inside the container. Finally, the -t flag is used to specify which image to run.

That’s it! You should see your container running when you check docker ps . Open up a web browser and navigate to http://localhost:8080. You should see the welcome page:

Learn more

This tutorial only scratches the surface of Docker and using containers to deploy ASP.NET Core applications. There’s plenty more to explore! Here are some links to get you started:

If you’re building applications in ASP.NET Core that need user authentication, check out my tutorial Build an ASP.NET Core application with user authentication. It only takes a few minutes to set up!

If you have questions about ASP.NET Core + Docker, leave me a comment below, or ping me on twitter at @nbarbettini. I’m happy to help!