Consultant and trainer.
Microsoft MVP.
Pluralsight Author.

  Learn Docker in a MoL Learn Docker in a Month of Lunches - the book
  Learn Kubernetes in a MoL Learn Kubernetes in a Month of Lunches - the book
  My Pluralsight Courses
Docker for .NET Apps - 8.5 hours of practical tutorials for 14.99!

Weekly Windows Dockerfile #5

There are 52 Dockerfiles in the source code for my book, Docker on Windows. Perfect for a year-long blog series.

Each week I'll look at one Dockerfile in detail, showing you what it does and how it works. This is #5 in the series.


Last week I covered a simple .NET Core Hello World app, where the Dockerfile had the steps to compile and package the app from source. That approach used Microsoft's .NET Core image with the SDK installed, so the final application image contained the SDK and the app source code - which isn't needed to run the app.

This week I'll show a different approach - compiling the app outside of Docker and then packaging the published output in the Dockerfile. The final application image uses Microsoft's .NET Core image with just the runtime installed, so the SDK and source code aren't part of the package.

The Dockerfile for this approach is in Dockerfile.slim, and it's very simple:

FROM microsoft/dotnet:1.1-runtime-nanoserver

WORKDIR /dotnetapp
COPY ./src/bin/Debug/netcoreapp1.1/publish .

CMD ["dotnet", "HelloWorld.NetCore.dll"]
  • FROM uses a version of the microsoft/dotnet image which has the .NET Core 1.1 runtime installed, but not the SDK. It's an image based on Nano Server so it runs as a Docker Windows container
  • WORKDIR creates a directory at C:\dotnetapp and sets that as the current working directory for the image
  • COPY copies the published application from the local machine into the Docker image
  • CMD specifies the command to run to start the app.

You can't build your own Docker image just by running docker image build with this version, you need to publish the application first. There's a build script which does all the steps:

dotnet restore src
dotnet publish src

docker image build --file Dockerfile.slim --tag dockeronwindows/ch02-dotnet-helloworld:slim .
  • the Dockerfile isn't called Dockerfile, so the build command uses the --file argument to specify the source file name

The dockeronwindows/ch02-dotnet-helloworld:slim image packages the published app binaries on top of an image that has the .NET Core runtime but not the SDK. That makes for a much smaller image - 1.15GB compared to 1.68GB for last week's version.

The drawback is that you need to have the right version of the .NET SDK installed on your dev machine to publish the app. You need the SDK on your build agent for the CI process too, and you also need to keep the toolchain in sync between the build server(s) and all the dev environments.


Well, first you need to install the .NET Core SDK. Then you run build.ps1 to publish the app and then build the Docker image:

Building the .NET Core Docker image

Then you can run the container. I've pushed a public image on Docker Hub, in the dockeronwindows organization, so you don't need to build it yourself, just run:

docker container run dockeronwindows/ch02-dotnet-helloworld:slim

Running the .NET Core app in a Docker Windows container

Packaging the app in an image with the .NET runtime but not the SDK makes for a smaller image, which is faster to push and pull. More importantly, it has a smaller surface area for attackers, and less software to be patched.

Next Up

Next week is the final take on the .NET Core Hello World app running in a Docker Windows container. Dockerfile.multistage uses Docker multi-stage builds to combine the best of the two other approaches. The app is compiled and published using Docker, so you don't need the .NET Core SDK installed to build the app, but the final image uses the minimal runtime base image.

Share this article on