ASP.NET Core in docker with GitLab Pipelines

A few months ago I have started working on a new, greenfield, project. Since it’s a one-man job, it was crucial for me to have an easy setup without big dev-ops needs - especially in the early beta stage. I decided to use Docker because I could automate the building process using pipelines in GitLab and Docker Cloud for deployment. Today I would like to show you how to setup Continous Integration for an ASP.NET Core project using GitLab pipelines.

  1. Creating new app
  2. Docker
    1. .dockerignore
    2. Dockerfile
    3. Running
  3. GitLab Pipelines
  4. Adding tests project
  5. Summary

Creating new app

To create a new application, you can use dotnet new web command. It creates an empty web application, which does one thing - displays famous Hello World. To start it you need to first restore all the dependencies with dotnet restore and then issue dotnet run:

dotnet new command


You now have an application. Let’s containerize it. To build a Docker image, you need to create a Dockerfile. It’s a set of instructions for Docker so it knows how to create an image. You can read more about Docker files in the official docs. There’s also a very nice post by Jakub Skałecki with tips on how to write better docker files.

Official Docker images for ASP.NET Core are available on the Docker Hub and there are few options to choose from:

  • microsoft/dotnet - this is a base .NET Core image. You can use it to build and run all types of .NET Core applications.
  • microsoft/aspnetcore - this image is optimized for running ASP.NET Core applications. It contains .NET Core and a set of native images for all of the ASP.NET Core libraries, so that these libraries don’t have to be compiled by JIT, which gives you faster cold start of your application.
  • microsoft/aspnetcore-build - it also contains all of the ASP.NET Core stuff, but also an SDK, which allows you to build apps in the container. It also has Node.js, Gulp and Bower.

Because we are going to use GitLab Pipelines, which don’t have dotnet SDK preinstalled, you will build the application in the container. Therefore you need the Microsoft/aspnetcore-build image. When the application is built you will use simple Microsoft/aspnetcore image for running the application.


Format and purpose of this file are identical to .gitignore. It’s a way to tell Docker, which files or directories you don’t want it to copy to your container. In our case, it’s just two folders for now. In a real app, you might want to add things like node_modules etc.



This is the most important file in the docker world. It tells the Docker engine how to build your image.

# Stage 1
FROM microsoft/aspnetcore-build AS builder
WORKDIR /source

# caches restore result by copying csproj file separately
COPY *.csproj .
RUN dotnet restore

# copies the rest of your code
COPY . .
RUN dotnet publish --output /app/ --configuration Release

# Stage 2
FROM microsoft/aspnetcore
COPY --from=builder /app .
ENTRYPOINT ["dotnet", "aspdocker.dll"]

As you can see, there are two stages. In the stage one we’re using the build image and then, when it’s done we copy the artefacts to the regular aspnetcore image.

First, we just copy the csproj file and run dotnet restore. If you wonder why we don’t copy the whole app at once, it’s because of the way docker caches stages during the build. If it detects the files didn’t change from the last build, it will use the cached image. Project files rarely change, but you probably will have changes in other files. Copying only the project file allows us to use cached stage from the dotnet restore command, which is time-consuming. Next, we copy the rest of the application and publish it to the /app folder.

The second stage uses plain aspnetcore image and gets build artefacts from the app folder.


You can test your new image by running: docker run -it -p 5000:80 aspdocker

GitLab Pipelines

Gitlab has an amazing feature of pipelines. It allows you to run jobs after each commit. You can use them to build your apps, run tests etc. Read more about them in their docs. It’s worth to notice it’s a free feature.

You define your build in the .gitlab-ci.yml. We will start with something, which will just build our docker image:

image: docker:latest

  DOCKER_DRIVER: overlay

- docker:dind

- build

  stage: build
  - docker build -t $CONTAINER_TEST_IMAGE .

This will simply run the docker build command in the context of our repository. When you commit this file to a GitLab repository, you will notice a green check in the commits list:

gitlab commit

Adding tests project

Now, when we have a running CI, which builds our project, it’s time to add support for tests. To do that, I have created an src directory, where I have moved the aspdocker project and added another one called aspdocker.tests. I also had to modify the Docker file a little to support different paths, but in general, to support running tests, I had to add one significant line to Dockerfile:

RUN dotnet test ./src/aspdocker.tests/aspdocker.tests.csproj

I have added it before the dotnet publish command so that if tests fail, the processing will stop and for a change, you will get red cross next to your commit:

pipeline failed

You can see all changes made here in the GitLab commit.


We now have an ASP.NET Core project, which is built and tested after each commit to the repository. We also have a docker image being built, which in the next post we will push to Docker hub repository and deploy to a Virtual Machine automatically using Docker Cloud.


Michał Dymel's Picture

About Michał Dymel

Passionate software developer interested in Web Development, .NET, Angular, architecture and security. Currently doing remote consulting.

Szczecin, Poland
Web Analytics