Version

There are multiple versions of this document. Pick the options that suit you best.

UI
Database
Tiered

Docker Deployment using Docker Compose

This document assumes that you prefer to use MVC / Razor Pages as the UI framework and Entity Framework Core as the database provider. For other options, please change the preference at the top of this document.

This guide will guide you through how to build docker images for your application and run on localhost using docker compose. You will learn the provided build scripts and docker compose files in detail and how to modify them for the production environment.

Building Docker Images

Each application contains a dockerfile called Dockerfile.local for building the docker image. As the naming implies, these Dockerfiles are not multi-stage Dockerfiles and require the project to be built in Release mode to create the image. Currently, if you are building your images using CI & CD pipeline, you either need to include the SDK to your pipeline before building the images or add your own multi-stage dockerfiles.

Since they are not multi-staged Dockerfiles, if you want to build the images individually, you can navigate to the related to-be-hosted application folder and run the following command:

dotnet publish -c Release

To populate the Release folder first, which will be used to build the docker images. Afterward, you can run the following command:

docker build -f Dockerfile.local -t mycompanyname/myappname:version .

To manually build your application image.

To ease the process, application templates provide a build script to build all the images with a single script under etc/build folder named build-images-locally.ps1. Based on your application name, UI and type, a build image script will be generated.

param ($version='latest')

$currentFolder = $PSScriptRoot
$slnFolder = Join-Path $currentFolder "../../"

Write-Host "********* BUILDING DbMigrator *********" -ForegroundColor Green
$dbMigratorFolder = Join-Path $slnFolder "src/Acme.BookStore.DbMigrator"
Set-Location $dbMigratorFolder
dotnet publish -c Release
docker build -f Dockerfile.local -t acme/bookstore-db-migrator:$version .

Write-Host "********* BUILDING Web Application *********" -ForegroundColor Green
$webFolder = Join-Path $slnFolder "src/Acme.BookStore.Web"
Set-Location $webFolder
dotnet publish -c Release
docker build -f Dockerfile.local -t acme/bookstore-web:$version .

	

### ALL COMPLETED
Write-Host "COMPLETED" -ForegroundColor Green
Set-Location $currentFolder

The image tag is set to latest by default. You can update the param $version at the first line to set it as a tag for your images.

You can examine all the provided Dockerfiles required to publish your application below;

DBMigrator

DbMigrator is a console application that is used to migrate the database of your application and seed the initial important data to run your application. Such as pre-defined languages, admin user and role, OpenIddict applications and scopes.

Dockerfile.local is provided under this project as below;

FROM mcr.microsoft.com/dotnet/aspnet:7.0
COPY bin/Release/net7.0/publish/ app/
WORKDIR /app
ENTRYPOINT ["dotnet", "BookStore.DbMigrator.dll"]

If you don't want to use the build-images-locally.ps1 to build the images or to build this image individually and manually, navigate to DbMigrator folder and run:

dotnet publish -c Release #Builds the projects in Release mode
docker build -f Dockerfile.local -t acme/bookstore-db-migrator:latest . #Builds the image with "latest" tag

MVC/Razor Pages

​ MVC/Razor Pages application is a server-side rendering application that contains both the OpenID-provider and the Http.Api endpoints within self; it will be a single application to deploy. Dockerfile.local is provided under this project as below;

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
COPY bin/Release/net7.0/publish/ app/
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
RUN dotnet dev-certs https -v -ep authserver.pfx -p 2D7AA457-5D33-48D6-936F-C48E5EF468ED

FROM base AS final
WORKDIR /app
COPY --from=build /src .

ENTRYPOINT ["dotnet", "Acme.BookStore.Web.dll"]

You can come across an error when the image is being built. This occurs because of dotnet dev-certs command trying to list the existing certificates inside the container and unavailable to. This error is not important since we aim to generate the authserver.pfx file and discard the container it is built in.

auth-server-pfx-generation-error

Since it contains the openid-provider within, it also uses multi-stages to generate authserver.pfx file which is used by OpenIddict as signing and encryption certificate. This configuration is found under the PreConfigureServices method of the WebModule:

if (!hostingEnvironment.IsDevelopment())
{
    PreConfigure<AbpOpenIddictAspNetCoreOptions>(options =>
    {
        options.AddDevelopmentEncryptionAndSigningCertificate = false;
    });

    PreConfigure<OpenIddictServerBuilder>(builder =>
    {
        builder.AddSigningCertificate(GetSigningCertificate(hostingEnvironment, configuration));
        builder.AddEncryptionCertificate(GetSigningCertificate(hostingEnvironment, configuration));
        builder.SetIssuer(new Uri(configuration["AuthServer:Authority"]));
    });
}

This configuration disables the DevelopmentEncryptionAndSigningCertificate and uses a self-signed certificate called authserver.pfx. for signing and encrypting the tokens. This certificate is created when the docker image is built using the dotnet dev-certs tooling. It is a sample-generated certificate, and it is recommended to update it for the production environment. You can check the OpenIddict Encryption and signing credentials documentation for different options and customization.

The GetSigningCertificate method is a private method located under the same WebModule:

private X509Certificate2 GetSigningCertificate(IWebHostEnvironment hostingEnv, IConfiguration configuration)
{
    var fileName = "authserver.pfx";
    var passPhrase = "2D7AA457-5D33-48D6-936F-C48E5EF468ED";
    var file = Path.Combine(hostingEnv.ContentRootPath, fileName);

    if (!File.Exists(file))
    {
        throw new FileNotFoundException($"Signing Certificate couldn't found: {file}");
    }

    return new X509Certificate2(file, passPhrase);
}

You can always create any self-signed certificate using any other tooling outside the Dockerfile. You need to remember to set them as embedded resource since the GetSigningCertificate method will be checking this file physically.

If you don't want to use the build-images-locally.ps1 to build the images or to build this image individually and manually, navigate to the Web folder and run:

dotnet publish -c Release #Builds the projects in Release mode
docker build -f Dockerfile.local -t acme/bookstore-web:latest . #Builds the image with "latest" tag

Running Docker-Compose on Localhost

Under the etc/docker folder, you can find the docker-compose.yml to run your application. To ease the running process, the template provides run-docker.ps1 (and run-docker.sh) scripts that handle the HTTPS certificate creation, which is used in environment variables;

$currentFolder = $PSScriptRoot

$slnFolder = Join-Path $currentFolder "../"
$certsFolder = Join-Path $currentFolder "certs"

If(!(Test-Path -Path $certsFolder))
{
    New-Item -ItemType Directory -Force -Path $certsFolder
    if(!(Test-Path -Path (Join-Path $certsFolder "localhost.pfx") -PathType Leaf)){
        Set-Location $certsFolder
        dotnet dev-certs https -v -ep localhost.pfx -p 91f91912-5ab0-49df-8166-23377efaf3cc -t        
    }
}

Set-Location $currentFolder
docker-compose up -d

run-docker.ps1 (or run-docker.sh) script will check if there is an existing dev-cert already under the etc/certs folder and generate a localhost.pfx file if it doesn't exist. Kestrel will use this file as an HTTPS certificate.

You can also manually create the localhost.pfx file in a different path with different name and a different password by using dotnet dev-certs https -v -ep myCert.pfx -p YOUR_PASSWORD_FOR_HTTPS_CERT -t or with using any other self-signed certificate generation tool.

You need to update the service environment variables Kestrel__Certificates__Default__Path with the path and filename you have created and the Kestrel__Certificates__Default__Password with your new password in the docker-compose.yml file.

Now lets break down each docker compose service under the docker-compose.yml file:

bookstore-web

bookstore-web:
    image: acme/bookstore-web:latest
    container_name: bookstore-web
    hostname: bookstore-web
    build:
      context: ../../
      dockerfile: src/Acme.BookStore.Web/Dockerfile.local
    environment:
      - ASPNETCORE_URLS=https://+:443;http://+:80;
      - Kestrel__Certificates__Default__Path=/root/certificate/localhost.pfx
      - Kestrel__Certificates__Default__Password=91f91912-5ab0-49df-8166-23377efaf3cc
      - App__SelfUrl=https://localhost:44353
      - AuthServer__RequireHttpsMetadata=false          
	  - AuthServer__Authority=http://bookstore-web  
      - App__HealthCheckUrl=http://bookstore-web/health-status      	
      - ConnectionStrings__Default=Data Source=sql-server;Initial Catalog=BookStore;User Id=sa;Password=myPassw0rd;MultipleActiveResultSets=true;TrustServerCertificate=True;			
    ports:
      - "44353:443"
    restart: on-failure
    volumes:
      - ./certs:/root/certificate
    networks:
      - abp-network

This is the MVC/Razor Page application docker service is using the acme/bookstore-web:latest image that we have built using the build-images-locally.ps1 script. It runs on https://localhost:44353 by default by mounting the self-signed certificate we've generated under the etc/certs folder.

  • App__SelfUrl points to the localhost with the port we expose https://localhost:44353. It must point to a real DNS when deploying to production.

  • AuthServer__RequireHttpsMetadata is the option for the openid-provider to enforce HTTPS. Since we are using an isolated internal docker network. We want to use HTTP in the internal network communication without SSL overhead; therefore it is set to false by default.

    • App__HealthCheckUrl is the health check url. Since this request will be done internally, it points to the service name in the containerized environment http://bookstore-api/health-status.

    • AuthServer__Authority is the issuer URL. http://bookstore-web is the containerized issuer. It must point to a real DNS when deploying to production.

  • ConnectionStrings__Default is the overridden default connection string. It uses the containerized sql-server with the sa user by default.

This service runs in Docker network called abp-network, awaits for the database container for starting up and restarts when it fails. You can customize these orchestration behaviors as you prefer.

db-migrator

db-migrator:
    image: acme/bookstore-db-migrator:latest
    container_name: db-migrator
    build:
      context: ../../
      dockerfile: src/BookStore.DbMigrator/Dockerfile.local
    environment:
      - OpenIddict__Applications__BookStore_Web__RootUrl=https://localhost:44353            
      - ConnectionStrings__Default=Data Source=sql-server;Initial Catalog=BookStore;User Id=sa;Password=myPassw0rd;MultipleActiveResultSets=true;TrustServerCertificate=True;            
    depends_on:     
      sql-server:
        condition: service_healthy                  
    networks:
      - abp-network

This is the database migrator service that migrates the database and seeds the initial data. OpenIddict data is one of the most important seeded data for your application to run. On production environment, you need to override the root URL of your application (https://localhost:44353) and the swagger-ui client URL (https://localhost:44354) so that the authentication can work properly.

This service runs in Docker network called abp-network, awaits for the database container to start up and restarts when it fails. You can customize these orchestration behaviors as you prefer.

What's next?

Was this page helpful?
Please make a selection.
Thank you for your valuable feedback!

Please note that although we cannot respond to feedback, our team will use your comments to improve the experience.

In this document
Mastering ABP Framework Book
Mastering ABP Framework

This book will help you gain a complete understanding of the framework and modern web application development techniques.