My Profile Photo

Stefan Prodan

Co-Founder of Veritech Solutions. Passionate about software architecture, domain driven design and DevOps. Loves programming in .NET, JS and Go

ASP.NET Core continuous deployment with Docker Hub

I’m working on an open source project, an ASP.NET Core app that monitors docker server activity. Since dockerdash needs to run in a container, I have to deliver it as a docker image on Docker Hub.

I was looking for a way to automate the deployment process in such a manner that my app would be built, tested and released to Docker Hub on every commit. Docker Hub can build images automatically from a GitHub repository as long as the repo contains a Dockerfile. An ASP.NET Core app can be built inside a docker container, so getting it to Docker Hub can be very easy.

The problem with this approach is that the dotnet onbuild image is very large, it’s over 500MB. For deployment, there is a different image available, that contains only the .NET Core (runtime and libraries) and it’s optimized for running portable .NET Core applications. Using the runtime image would require the code to be compiled and the compiled output to be copied inside the image.

Docker Hub can’t compile your app outside of a container, for that you need a build server. You can use AppVeyor build server, it has a free subscription plan for open source project and it can build, test and deploy .NET Core apps. But you can’t build and push a docker image with AppVeyor, only .net code. My solution is to get the build artifacts out of AppVeyor and back on GitHub on a different branch using AppVeyor git push. Docker Hub will detect a commit on that branch, where the artifacts are, and use them to build the image.

CD Pipeline

Continuous deployment pipeline:

  • You commit changes to master branch
  • AppVeyor automatically pulls the master branch
  • AppVeyor executes the build script
  • The build script invokes dotnet restore
  • The build script invokes dotnet build
  • The build script invokes dotnet test
  • The build script invokes dotnet publish
  • AppVeyor commits changes(dotnet publish output) to local branch
  • AppVeyor pushes the commit to the release branch
  • Docker Hub automatically pulls the release branch
  • Docker Hub builds the image using the Dockerfile
  • Docker Hub publishes the image under the latest tag

AppVeyor build script example:

function EnsurePsbuildInstalled{
        [string]$psbuildInstallUri = ''
        if(-not (Get-Command "Invoke-MsBuild" -errorAction SilentlyContinue)){
            'Installing psbuild from [{0}]' -f $psbuildInstallUri | Write-Verbose
            (new-object Net.WebClient).DownloadString($psbuildInstallUri) | iex
            'psbuild already loaded, skipping download' | Write-Verbose

        # make sure it's loaded and throw if not
        if(-not (Get-Command "Invoke-MsBuild" -errorAction SilentlyContinue)){
            throw ('Unable to install/load psbuild from [{0}]' -f $psbuildInstallUri)

function Exec
        [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
    & $cmd
    if ($lastexitcode -ne 0) {
        throw ("Exec: " + $errorMessage)

if(Test-Path .\release) { Remove-Item .\release -Force -Recurse }


exec { & dotnet restore }


$revision = @{ $true = $env:APPVEYOR_BUILD_NUMBER; $false = 1 }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL];
$revision = "{0:D4}" -f [convert]::ToInt32($revision, 10)

exec { & dotnet restore .\src\APP }
exec { & dotnet test .\src\APP.TESTS }
exec { & dotnet build .\src\APP }

$release = Join-Path $pwd release
exec { & dotnet publish .\src\APP -c Release -o $release --version-suffix=$revision}

Replace APP with your app name. For more details on the build script you can check jbogard/MediatR repo.

AppVeyor config file example:

version: '{build}'
  do_not_increment_build_number: true
  - master
  disable_publish_on_pr: true
- ps: .\Build.ps1
test: off
    secure: UdsFh1+gLuPPgH0byZBBxH7Ue6tILfmpflXwZJ0ZZu0XeQ2dANyLeN/fWWpKVcOy
    secure: nq2fO/Zi7xCmkD38qvSMsjT/f/Hqpb87wx9Ci4VIjmA=
  - git config --global credential.helper store
  - ps: Add-Content "$env:USERPROFILE\.git-credentials" "https://$($env:access_token):[email protected]`n"
  - git config --global "$($env:git_email)"
  - git config --global "Your name"
  - git config --global core.autocrlf true
  - git checkout master
  - git add .
  - git commit -m "ci deploy"
  - git status
  - git push origin master:release -f

Replace access_token, git_email and with your values. You can check the AppVeyor tutorial on pushing to remote Git repository from a build for more details.

Dockerfile example:

FROM microsoft/aspnetcore:1.0.1

# Set ASP.NET Core environment variables
ENV ASPNETCORE_URLS="http://*:5000"

# Copy files to app directory
COPY /release /app

# Set working directory

# Open port
EXPOSE 5000/tcp

# Run
ENTRYPOINT ["dotnet", "APP.dll"]

Replace APP with your app name.

Docker Hub build settings example:

CD Pipeline

A working example of this CD pipeline is available on GitHub at stefanprodan/dockerdash.

comments powered by Disqus