Running .NET Core on Docker

It’s a new era for Microsoft and for .NET, and that is not an overstatement. If you haven’t been following, here are some of the things that happened over the last year:

And with the new .NET Foundation the whole ecosystem is not even owned by Microsoft anymore.

Putting all of this together you get a full experience where you can develop with C# on either Windows or Mac, use proper command-line UNIX-based developer tools even on Windows, and happily deploy to Linux servers. Basically .NET/C# has overtaken Java as the true cross-platform development ecosystem.


In order to get experience with the new toys, I’ve come up with the following plan:

  1. Create a “hello world” web service using .NET Core and C# on Mac
  2. Run it in docker
  3. Deploy it to AWS container service

Let’s get our hands dirty…

Step 1: Set up

.NET Core

So I decided to use Mac OSX rather than Windows to make it more interesting (plus it will make life easier when trying to run docker). Installing .NET is super easy just following the steps here

.NET Core installation steps

That gives you dotnet command line tool which you can use to create, build, run projects without any IDE. Here is how you create and run a new “Hello World” console app:

Visual Studio Code

If you like using plain text editors, that’s all you need. I prefer nicer IDE experience, and luckily there is Visual Studio Code editor which works on Windows, OSX and Linux (not to be confused with Visual Studio full IDE which is Windows-only). Visual Studio Code is a lightweight but powerful general-purpose text editor, with plugins for various languages including C#. Again, it’s very easy to install from:

Visual Studio Code download

and you should add C# extension after launching (press F1 and type “install extension”). It’s worth reading this to get a better idea how VS Code works:

Visual Studio Code basics

Once you open the .NET project in VS Code, you get highlighting, statement autocompletion, and debugging!

That’s all the tools you need to go on and develop a full-blown .NET project — all on Windows, OSX or Linux!

Step 2: ASP.NET Core

Our goal is to have a web service rather than a console app. In addition to .NET Core there is a lean web framework ASP.NET Core (completely rewritten compared to original ASP.NET). We can convert our test app to a web service following the steps here:

ASP.NET Core getting started guide

  1. Add Microsoft.AspNetCore.Server.Kestrel dependency to project.json
  2. Add Startup.cs class with sample request handler
  3. Launch Web server in Main()

That’s it, executing dotnet run from command line or F5 from VS Code will start up the web host listening for HTTP requests.

Think about it — no Windows, no IIS, no installations, you get a full stand-alone web app in 10 lines of code!

Interlude: NuGet / .NET Framework / .NET Core / .NET Standard, etc

So far I haven’t really explained what is .NET Core and how it relates to the usual .NET Framework. There are a lot of changes, and it’s quite confusing at times (not helped by the fact that some tools and naming has changed from version alpha to beta to RC1 to RC2). Here is the official overview, just keep in mind that version “5” has been dropped, so “ASP.NET 5” is actually “ASP.NET Core”. It’s difficult to tell the complete story, so let me just mention a few points that I’ve learned along the way:

  • .NET Core is essentially a port of the .NET Framework 4.6 with all the Windows dependencies removed so it can run on Linux and OSX. It is in active development as an open source project on GitHub. That includes Runtime for different platforms and cross-platform standard libraries.
  • .NET Core libraries contain a subset of the full .NET Framework, and there are some parts that are not yet ported, or never will be (System.Web and ASP.NET WebForms, for example). That means that porting existing projects is not completely trivial, if they use dependencies that don’t exist in .NET Core. Some info about transitioning here.
  • Once you create a new application, you will see project.json file instead of the old csproj MSBuild-based project file. It describes dependencies, target platforms, etc, however, unlike csproj, it doesn’t list all *.cs files in the project — the project is just everything in the directory.
  • .NET Core uses NuGet to manage all dependencies, including standard library, which makes it fully modular. For example, by default project.json references Microsoft.NETCore.App NuGet dependency, which is the wrapper for all standard libraries in .NET Core. Check out the dependency list in the NuGet page — you will see System.Net, System.Linq, etc — all as NuGet packages!
  • There are various “target frameworks” for which you can build your app, including .NET Core (netcoreapp), .NET Framework (net), Universal Windows Platform (uwp), Mono Android (monoandroid), etc. That’s indicated in project.json (e.g. netcoreapp1.0) and NuGet handles the dependencies based on target. Check the list of dependencies for any new package in NuGet — you will see them grouped by target framework.
  • .NET Standard 1.X target framework is a unified set of features supported by .NET on different target frameworks, and the new NuGet packages will try to target that, which means they can run on any compatible frameworks. Here is the explanation and detailed information about different frameworks and versions.
  • DNXCore 5.0 (dnxcore50) is the deprecated name for .NET Core, but some NuGet packages use that, and the line {“imports”: ”dnxcore50"} in project.json says that just use that version if you need .NET Core.
  • If you have some 3rd party NuGet dependencies like Json.NET, Dapper, Azure SDK, etc, and wonder if they will run on .NET Core — just check if they have .NET Standard target on the NuGet page. Most of the actively developed packages have just released (as of May 2016) beta or RC versions targeting .NET Standard / .NET Core.
  • ASP.NET Core is a completely new web framework, having almost nothing in common with the previous ASP.NET. It’s supposedly much leaner and faster, and uses self-hosted Kestrel server which runs on any platform and is based on libuv. There is alredy some documentation here.

That’s some basic info to get started and there is, of course, a lot more to learn — I’ve included some references at the bottom. But let’s carry on.

Step 3: Docker setup

Let’s now try to run our .NET web service on Docker. Full disclosure — I’ve never actually tried running Docker on Mac until today. Start by installing Docker:

Docker installation on Mac OS X

If everything goes smoothly, run docker run hello-world

Now coming back to .NET, there is an official .NET Core container on DockerHub. Let’s run it:

Ok, so we’ve got .NET to run inside Linux Docker container!

Step 4: Create Docker image

Next step is to package our .NET web service application (built on Mac) into a Docker container.

First, in order to build a deployable .NET package run dotnet publish

This copies all the dependencies into publish directory, except the .NET Core framework itself. The output will contain testapp.dll file, which is launched by executing dotnet testapp.dll. Actually, there is a way to publish “native” deployable, which will include the .NET framework and the binary executable for the target system, but we are not doing that here.

Once the app is built, we package it into a container with the following Dockerfile

FROM microsoft/dotnet:latest
COPY bin/Debug/netcoreapp1.0/publish/ /root/
EXPOSE 5000/tcp
ENTRYPOINT dotnet /root/testapp.dll

This takes the base microsoft/dotnet image, copies the publish output directory, exposes 5000 port and executes .NET app.

Then build the container (docker build -t testapp .) and run (docker run -it -p 5000:5000 testapp)

Looks like it worked! The only problem turns out that the web server is listening only on localhost:5000, so it’s not responding to requests from outside. We need to tweak this in Program.cs by specifying *:5000 as the listening address

Running through (publish > docker build > docker run) process again


Step 5: running on AWS

One annoyance with running .NET services in the past was that you have to deploy them as Windows Services on some shared host. This introduced various problems such as lack of isolation, lack of individual monitoring, cumbersome deploy scripts, etc. When running in the cloud, an alternative is to have a separate VM for each service, but that’s quite an overkill which doesn’t use resources effectively (especially running on Windows host) plus makes deployment time very long.

Container services seem to offer a perfect solution: you have a cluster of hosts, where you deploy each service as a stand-alone container. Of course, that only really works with Linux, and now with .NET Core that opens the possibility to use this strategy with .NET services too!

There are various options for container services, but since at TRAFI we run everything on Amazon Web Services (AWS), let’s try their Amazon EC2 Container Service (ECS).

We follow “Getting Started” steps in ECS, which helps to set up AWS CLI, create AWS Docker repository, and push our local Docker image to the AWS repository.

In order to run a container, you describe it as Task Definition which includes port mappings, and resource limits (memory, CPU, etc)

and create a cluster where tasks (containers) will be hosted

Once the cluster is up, we can just run a new container task into the cluster:

It starts really quickly, and we’ve got our web service up and running in the cloud!

That’s just the basic usage, but then you’ve got all the options of running multiple instances of tasks, load-balancing between them, automatically restarting failed instances, zero-downtime deployment, etc, etc.

In addition, containers make the build and deployment flow nice and clean — one can have a CI build which builds and uploads Docker containers, and then a deployment is just a single AWS CLI command that restarts the task in the cluster using the new container revision.


It really feels that all the pieces are coming together in order for .NET and C# to provide a modern cross-platform development platform. I’m really excited to go ahead and apply this new workflow (.NET Core + Docker + AWS container service) in production at TRAFI.

Of course, this was just a Hello World app — it’s still early times, and there could still be problems when building larger applications. But with the current RC2 release things should be pretty much ready for production, and the final 1.0 version is coming really soon (here is the plan). In general, Microsoft seems really committed to the new direction, the new community seems to be growing, and it looks like things will only get better.


Keep in mind that most of the information is very new, and a lot of things have changed during development over the last year, including naming (e.g. ASP.NET 5 to ASP.NET Core) and breaking API changes. It’s finally stabilised now, but there is a lot of documentation and tutorials which are already outdated.