Running ASP.NET Web API in Docker


With all the hype around Docker lately, I’ve felt a bit out of the loop.  Rather than continuing to sit around, wondering what it’s all about, I decided to finally take a look for myself.

I quickly found that the platform has enormous benefits to supporters and users of the various open source communities that run on the Linux platform.  It enables them to create one deployment package for our code that can run on Unix, Linux, Amazon AWS, and Windows.  The benefits of such a platform are somewhat apparent just when considering the platform.  But what’s in it for us .NET and Microsoft developers?  That is exactly what I desired to find out and is exactly what I aim to cover in this blog post.


Overview of Docker

In this post, I will not cover a soup to nuts overview of Docker.  The focus of this post will be on how to host ASP.NET WebAPI in Docker.  Thus, the overview, herein, will be somewhat brief.  If you would like more information on the core concepts behind Docker, I recommend you start with the Docker Documentation.

At the core, Docker is a virtualization platform.  It enables you as a developer to create your solution and target it towards a virtual environment that has all of the components necessary to run your code (excluding DB, service, and other network dependencies of course).  This virtual environment that you run your code within is known as a Docker container.

At the heart of the Docker platform is the concept of the Docker container.  Here is the description straight from the Docker website:

Docker containers wrap up a piece of software in a complete file system that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in


Alternatively, consider this definition from Scott Hanselman’s post:

Docker containers are sandboxes running on the same OS kernel. They are easy to deploy and start fast. […]  Containers start fast and the underlying shared resources are what’s maintained and kept up to date.

[ref: See resources section at end of post]

For a better understanding of Docker containers, let’s consider a simple metaphor.


The Transportation Metaphor for Docker Containers

In order to find a great metaphor for describing the recent adoption of Docker, consider the history of the universal container that is currently used in most transportation operations today.

Let’s take a look back in time and see what the logistics situation looked like in the transportation industry as materials were shipped over water, rail, and road:


At one point in time, it was necessary for people to transfer indivual packages, crates, and other shipping materials from one transport mechanism to another.  There are some obvious bottlenecks with the transportation flow above and the solution seems obvious.  However, at the time, it took some creativity and motivation to make the modern universal container possible.  Now here’s a look the modern day transportation solution:


So how does this relate to Docker?  Well, one way to put it is that the Docker container provides a mechanism, similar to the containers of the transportation industry, for which we can easily and quickly run our code on various platforms without the need to unpack the contents of the container and redeploy.


With Docker, you can take a container, which contains all of the necessary libraries and file system dependencies necessary to run your code, and move it to any platform that supports hosting Docker containers.  This includes, but is not limited to, Linux, Amazon AWS, and Microsoft Azure.



The solution I will be presenting in this post is a simple order API that can be used to create entries in an order system.  The API is implemented using ASP.NET Web API, which I will be hosting in Docker.

The backend system where the orders will be stored will be MongoDB.  The MongoDB instance will also be hosted in Docker.

So let’s get started on taking a look into how we can get all of this up and running.


Host ASP.NET Web API in Docker

If you would like to follow along, I have a couple disclaimers.  All of steps described here will be specific to OSX.  You should be able to run this demo on Windows, but there will be several discrepancies in the command line tools and syntax used.

To follow along with this post, you will first need to install Docker on your machine.  You can find the latest Docker installers for OSX and Windows here:

To get started, let’s first go grab all of the code for this solution from GitHub:

$ git clone
Cloning into 'docker-webapi-example'...
remote: Counting objects: 52, done.
remote: Compressing objects: 100% (39/39), done.
remote: Total 52 (delta 10), reused 42 (delta 5), pack-reused 0
Unpacking objects: 100% (52/52), done.
Checking connectivity... done.

Next, let’s fire up the docker terminal…

Screenshot 2015-09-26 18.31.30

Now that we’re in the docker terminal, let’s navigate to the directory where we just grabbed all the code from GitHub.  Next, we’ll take a look at how to get MongoDB up and running in Docker.


Running MongoDB

Before we begin setting up our API project, let’s first start up our MongoDB instance.  From the command line, run the following command:

$ docker pull mongo

By executing this command, we tell docker to go fetch the mongo image from the Docker Hub Repository.  So what is Docker Hub, you ask…  According to Docker documentation, it is “a centralized resource for container image discovery, distribution and change management, user and team collaboration, and workflow automation throughout the development pipeline”.  You can use Docker Hub to find a number of official Docker images, such as the mongo image we just used and the mono image that we will be using later in this demo.

Now, to start the MongoDB instance, I will use the docker run command along with a few parameters.

$ docker run --name ordersdb -p 27017:27017 -d mongo

In this command, we told docker to start the Docker image named “mongo” in a Docker container named “ordersdb” with port 27017 of the host environment mapped to port 27017 within the container that is being hosted.  Port 27017 is the default port on which MongoDB runs.  In addition to the name and port information, we supplied the ‘-d’ argument, which tells docker to run mongo as a daemon (or service if you will).

We can now run “docker ps” to print out the running containers, which should consist only of the mongo image named “ordersdb”.

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                      NAMES
4c5d1e24db42        mongo               / mongo     3 seconds ago       Up 3 seconds    ordersdb


Building the Web API Image

With the MongoDB instance running, we are now ready to kick off our Web API solution in Docker.

First, make sure your command terminal is in the directory where we downloaded all of the sources form GitHub.  Then, navigate into the Poc.Docker.OrderApi directory.

Once you’re in the Poc.Docker.OrderApi directory, run the following docker build command in order to create the Docker image which will contain our Web API solution.

Poc.Docker.OrderApi$ docker build -t jixer/webapidemo .

You should see a lot of output which reflects various files being downloaded and installed as well as some output specifying that our solution is being compiled / installed.  So what exactly did we just do?

Let’s start by taking a look at the Docker file in that directory.  The Dockerfile is the configuration file that tells the Docker build command what exactly it is supposed to do.  Here are the contents of that file:

FROM mono:onbuild


CMD ["mono", "./Poc.Docker.OrderApi.exe"]

Let’s break it down line by line.

Line 1 is what tells Docker the base image that our new image will be based on.  In this case we have used the mono image (which is located in Docker Hub) along with the onbuild tag.  The onbuild tag is an important part.  The mono image has an onbuild feature which takes all of the .NET source code from the current directory, moves it into the Docker container, and then compiles the .NET SLN using mono.  This is why we never needed to compile our solution prior to creating the Docker image based on mono.

Line 3 is responsible for telling Docker that we will be exposing a port.  In our case, port 8080 will be the port that Web API will listen to for our orders.

Line 5 is the last and final entry, which tells Docker the entry point of our application.  This line (CMD) specifies to Docker that when containers that are based upon our new image are run, the first command they should run is “mono ./Poc.Docker.OrderApi.exe”.  This line is responsible for starting our self hosted Web API console application.

Now if you skim through output produced by docker build, you should see several of these steps executing:

Sending build context to Docker daemon 27.65 kB
Step 0 : FROM mono:onbuild
onbuild: Pulling from library/mono

b92a854a78d0: Pull complete 
63e9265ef57c: Pull complete 
18e3a2e19027: Pull complete 

... redacted ...

library/mono:onbuild: The image you are pulling has been verified. Important: image verification is a tech preview feature and should not be relied on to provide security.

Digest: sha256:9d43a20b74e8d02fea4f2fe7f65447c4c374d414fc9836c69df9f3c0163cbc1d
Status: Downloaded newer image for mono:onbuild
# Executing 4 build triggers
Trigger 0, COPY . /usr/src/app/source
Step 0 : COPY . /usr/src/app/source
Trigger 1, RUN nuget restore -NonInteractive
Step 0 : RUN nuget restore -NonInteractive
 ---> Running in ef6964370b2e
Installing 'Microsoft.AspNet.WebApi.Owin 5.2.3'.
Installing 'Microsoft.AspNet.WebApi.Core 5.2.3'.
Installing 'Microsoft.AspNet.WebApi.Client 5.2.3'.
Installing 'Microsoft.Owin.Host.HttpListener 2.0.2'.
Installing 'Microsoft.Owin.Hosting 2.0.2'.

... redacted ...

Build started 09/27/2015 01:50:32.
Project "/usr/src/app/source/Poc.Docker.OrderApi.sln" (default target(s)):
Target ValidateSolutionConfiguration:
Building solution configuration "Release|Any CPU".

... redacted ...

Step 1 : EXPOSE 8080

... redacted ...

Step 2 : CMD mono ./Poc.Docker.OrderApi.exe
 ---> Running in 558a9d4dfe73
 ---> 0616831ec17e
Removing intermediate container 558a9d4dfe73
Successfully built 0616831ec17e

Last, but not least, if we run the Docker command to list all available images (“docker images”), we should see the image we just created listed:

MacBook:Poc.Docker.OrderApi$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
jixer/webapidemo    latest              0616831ec17e        13 minutes ago      642.7 MB
mongo               latest              b86849b1ee30        5 days ago          260.7 MB
mono                onbuild             a64d24bff5e2        2 weeks ago         622.5 MB


Running the Order API Solution

With the confirmation that our image was created successfully, we are ready to work on running the Orders API solution.  To do so, we will leverage the “docker run” command, similar to how we started our MongoDB instance.  However, this time we will specify a different name and a different set of virtual ports.

Poc.Docker.OrderApi$ docker run --name ordersapi -p 8080:8080 -d jixer/webapidemo

With that, our Docker container should now be running.  Let’s use docker ps to confirm that.

Poc.Docker.OrderApi$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                      NAMES
5f33f1cdf609        jixer/webapidemo    "mono ./Poc.Docker.Or"   12 seconds ago      Up 11 seconds>8080/tcp     ordersapi
4c5d1e24db42        mongo               "/ mongo"   25 minutes ago      Up 25 minutes>27017/tcp   ordersdb

And there we see it. Our container named “ordersapi” is up and running for 11 seconds.  I think we our now ready to test the API to see if our API is now workinging.


Testing the Orders API

For this section of the post, I will be using Curl in order to test the HTTP hosted Orders API.  I’m using curl as it comes equipped in the Mac OSX terminal, but Fiddler or any other HTTP testing tool will suffice.

The endpoint for the API if you are using OSX will be

The IP address is generated by the Docker host and may vary by environment.  If you’re using Windows, you may need to refer to the Docker documentation to determine which IP the Docker host is running on.  When you first start the Docker terminal, it should output this information.  For example, on Mac, the initial welcome message I received when I started the Docker Terminal was as follows:

docker is configured to use the default machine with IP
For help getting started, check out the docs at

Let’s first test our create order API by sending an HTTP POST message to the endpoint along with some JSON data that represents the order we wish to create:

$ curl -H "Content-Type: application/json" --data '{"FirstName": "David", "LastName": "Ricardo", "Address": {"Street1": "123 Abc St", "City": "Phoenix", "State": "AZ", "Zip": "123456"}, "ItemId": "21a0276a-ff97-4d5a-828b-ae13024f4aec", "Quantity": 5}'

The response returned from the API is a JSON string that represents the transaction ID of the order that was just created.  In my case, the GUID was 7a88b97f-f32c-4a4d-b852-4155d1215cc4.  I can now use that along with HTTP GET in order to fetch the order from our database.

$ curl
{"Id":"7a88b97f-f32c-4a4d-b852-4155d1215cc4","FirstName":"David","LastName":"Ricardo","Address":{"Street1":"123 Abc St","Street2":null,"City":"Phoenix","State":"AZ","Zip":"123456"},"ItemId":"21a0276a-ff97-4d5a-828b-ae13024f4aec","Quantity":5}

As you can see in the response, the API was able to retrieve the record we previously stored in MongoDB and then returned it in the body of the response message.


The Web API Orders Solution

In the previous section, we ran our Web API solution via the Docker “run” command.  Now, let’s take a look at the underlying solution.

Here are the main contents of the solution:

Screenshot 2015-09-27 19.25.53

As you may be able to tell, so far we just looking at a fairly straight forward Console Application.  Let’s take a look at some of the NuGet packages in order to see what’s happening behind the scenes.

Screenshot 2015-09-27 19.23.11

Main takeaway here is that we’re leveraging the Microsoft ASP.NET Web API OWIN Self Host in order to host our API in a console application.  This is how we’re able to achieve hosting our solution in a Console Application running on top of Mono without any of the IIS dependencies.  As of ASP.NET 4, this is the easiest way I’ve found yet to host Web API in Docker via the mono Docker image.

However, with the introduction of ASP.NET 5 (vNext) the .NET Execution Environment enables us to self host just about all aspects of ASP.NET (i.e., Web API, MVC) in Docker.  ASP.NET 5 is currently slated for release in November 2015.

In this blog post, I decided not to provide another example of how to run ASP.NET 5 in Docker as there are several examples already available.  If you would like to see an example of how to host MVC in Docker (with ASP.NET 5), you should check out this great article by Scott Hanselman that describes how to do so:

I won’t go into detail of the code in my solution, as it is beyond the scope of this article. But, if you would like to browse on your own, start with Program.cs as it is the main entry point.  Furthermore, the main implementation of our API is in the API controller named, “OrderController.cs”.  Lastly, Startup.cs is the configuration entry point for the OWIN self hosting model.



If you would like to get more hands on with running other types of .NET solutions in Docker, the following is a good list of articles and tutorials to get you started.

Docker Docs

Core documentation for Docker.


Docker Hub

The Docker Hub is a cloud-based registry service for building and shipping application or service containers.  It provides a great location to browse official Docker repositories and locate existing docker containers.


Running ASP.NET 5 applications in Linux Containers with Docker

Article on MSDN Blogs that describes how to get ASP.NET 5 up and running with the aspnet-docker solution and new VisualStudio Docker tooling (currently in preview).


Publishing an ASP.NET 5 app to Docker on Linux with Visual Studio

Post by Scott Hanselmann on how to a get an ASP.NET 5 application running up and running quickly and easily using Docker.


GitHub Page for .NET Execution Environment

According to this GitHub page, the .NET Execution Environment contains the code required to bootstrap and run an application. As you saw in this blog post, you can host Web API in Docker via the OWIN Self Host.  Alternatively, we could have hosted ASP.NET 5 in Docker through the .NET Execution Environment, as shown in Scott Hanselman’s post.



I hope you enjoyed this post and walk away with a better idea of Docker as well as how it can be used to create a container for your ASP.NET Web API applications that can be hosted on just about any on-premise and cloud systems.

Please use the social icons below to share this post with your friends and colleagues.  Also, if you have questions or things that you would like to add to this post, please use the comments below.

Add your comment Add your comment