A Multi-stage Dockerfile for .Net Core

Amin KB
3 min readJan 10, 2021

How to write a multi-purpose Dockerfile for .Net Core Projects?

Dockerfile dotnet core SDK image

Let’s say you have an application created by .Net Core 3.1 and you wish to create a Dockerfile for it.

I know there are many Dockerfiles ready on the internet that you can choose from, but I am going to explain how to create a suitable one for development and a more efficient one for production.

Here, I am going to declare three stages for this purpose

  • Developer stage
  • Builder stage
  • Final stage

And the full Dockerfile content is something like the following

ARG REPO=mcr.microsoft.com/dotnet/core/sdk
FROM $REPO:3.1-bionic AS developer
# Prepare Dotnet Entity Framework
RUN dotnet tool install --global dotnet-ef
ENV PATH="${PATH}:/root/.dotnet/tools"
# # #
# Placeholder for any dev tools
# # #
# Copy csproj and restore as distinct layers
COPY ./src/Myapplication.Web.Host/Myapplication.Web.Host.csproj \
./host/Myapplication.Web.Host/
COPY ./src/Myapplication.Web.Core/Myapplication.Web.Core.csproj \
./host/Myapplication.Web.Core/
COPY ./src/Myapplication.Core/Myapplication.Core.csproj \
./host/Myapplication.Core/
COPY ./src/Myapplication.Application/*.csproj \
./host/Myapplication.Application/
COPY ./src/Myapplication.EntityFrameworkCore/*.csproj \
./host/Myapplication.EntityFrameworkCore/
RUN dotnet restore \
./host/Myapplication.Web.Host/Myapplication.Web.Host.csproj
WORKDIR /src
COPY ./runapp.sh ./runapp.sh
RUN chmod +x ./runapp.sh
FROM developer AS builderARG BUILDCONFIG=RELEASE
ARG VERSION=1.0.0
RUN export ASPNETCORE_ENVIRONMENT=Staging# copy everything else and build
COPY ./src/ ./host/
RUN dotnet publish \
host/Myapplication.Web.Host/Myapplication.Web.Host.csproj \
--runtime linux-musl-x64 -c $BUILDCONFIG -o /out \
/p:Version=$VERSION -p:PublishTrimmed=true
RUN dotnet publish \
host/Myapplication.Migrator/Myapplication.Migrator.csproj \
--runtime linux-musl-x64 -c $BUILDCONFIG -o /out/Migrator \
/p:Version=$VERSION -p:PublishTrimmed=true
# Final runtime image
FROM mcr.microsoft.com/dotnet/core/runtime-deps:3.1-alpine
RUN apk add icu-libs
WORKDIR /app
COPY --from=builder /out ./
ENV ASPNETCORE_URLS=http://0.0.0.0:80
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
ENTRYPOINT ["./Myapplication.Web.Host"]

Developer stage

Myapplication is a .Net Core solution with multiple projects inside. In the first stage named developer I am using Ubuntu bionic based provided .Net Core SDK.

This is important to use Ubuntu here if you wish to have a remote connection to the container and you could be able to debug via VSCode. Somehow, I can’t manage to install OmniSharp properly on the Alpine image. Although Ubuntu's image seems to be heavy, in action there are not so many differences for a developer.

In developerstage, dotnet tool install helps to install the Entity framework (dotnet-ef).

Next, will just copy project files to run a restore command on dotnet, so, you will have all the required NuGet packages installed on the image.

Need to mention here, I have provided a shell file runapp.sh to automated some required commands which is a command to migrate the database by Entity Framework, and a command to execute dotnet run command.

Builder stage

Basically, the builder stage is a replacement for build pipelines for me here. I mean it’s an easy way to implement a build procedure to validate build and finally publish a prod version of the application.

Here, the build stage is starting from the developer stage. Setting the environment variables, and running the publish command by dotnet. Because I have two applications to be published I have two dotnet publish commands here. My prod environment identification is Staging which is set by environment variable ASPNETCORE_ENVIRONMENT and it takes appsettings.Staging.json as the main configuration file while running.

Final stage

Finally, it’s time to create a production version of the application image.

By taking the Alpine runtime version of .Net Core, we can manage to create a very efficient version of our .Net Core application which uses less memory and space than Debian images.

Remember, keep ASPNETCORE_URLS as 0.0.0.0 to accept all incoming IP’s. And, the image is ready to be published wherever on production Docker host or Kubernetes.

Other resources:

And later I will discuss the following topics as well

  • Docker-compose Development Environment
  • Using Azure DevOps Deploy to GKE (Google Kubernetes Engine)

--

--