Skip to content

Docker & Containers Guide — Progress CU Platform

Last updated: 2026-03-23

1. Dockerfile Structure

The platform uses a two-stage build pattern based on .NET 10 images:

Stage Base Image Purpose
build mcr.microsoft.com/dotnet/sdk:10.0 Restore, compile, publish
final mcr.microsoft.com/dotnet/aspnet:10.0 Runtime only (~220 MB)

The build stage also installs Node.js 18 for building the custom property editors (TypeScript/Vite) in Progress.CustomPropertyEditors.

Layer caching strategy:

  1. Copy .csproj files first, run dotnet restore (cached unless project refs change)
  2. Copy package.json / package-lock.json, run npm ci (cached unless deps change)
  3. Copy all source, run dotnet publish

Template Dockerfile

The template at dbl.Progress/templates/Progress.Template/Dockerfile is a simplified version without Node.js — used for new client projects that don't yet have custom property editors. It reads from a nuget.config for the internal feed.

2. Build Arguments

ARG Default Description
CONFIGURATION Debug .NET build configuration (Debug or Release)
ASPNETCORE_ENVIRONMENT Debug Runtime environment (Development, Staging, Production)

The template Dockerfile defaults to Release / Production.

3. Building Locally

# Debug build (default)
docker build -t progress-cu:dev ./dbl.Progress

# Release build
docker build \
  --build-arg CONFIGURATION=Release \
  --build-arg ASPNETCORE_ENVIRONMENT=Production \
  -t progress-cu:latest \
  ./dbl.Progress

# Build with no cache (after Dockerfile changes)
docker build --no-cache -t progress-cu:dev ./dbl.Progress

Build context is ./dbl.Progress. The .dockerignore excludes bin/, obj/, node_modules/, .git/, docs/, psCreditUnion/, double-migration-tool/, and external-references/.

4. Running Locally

# Minimal — connects to an existing SQL Server
docker run -d \
  --name progress-cu \
  -p 8080:8080 \
  -e ASPNETCORE_ENVIRONMENT=Development \
  -e ASPNETCORE_HTTP_PORTS=8080 \
  -e "ConnectionStrings__umbracoDbDSN=Server=host.docker.internal;Database=dbl_progress;User Id=sa;Password=YourPassword;TrustServerCertificate=True" \
  -e "ConnectionStrings__umbracoDbDSN_ProviderName=Microsoft.Data.SqlClient" \
  -e Umbraco__CMS__Unattended__InstallUnattended=true \
  -e Umbraco__CMS__Global__UseHttps=false \
  progress-cu:dev

# Access at http://localhost:8080
# Backoffice at http://localhost:8080/umbraco

With volume mounts

docker run -d \
  --name progress-cu \
  -p 8080:8080 \
  -v progress-media:/app/wwwroot/media \
  -v progress-logs:/app/umbraco/Logs \
  -v progress-keys:/app/DataProtection-Keys \
  -e ASPNETCORE_ENVIRONMENT=Development \
  -e ASPNETCORE_HTTP_PORTS=8080 \
  -e "ConnectionStrings__umbracoDbDSN=Server=host.docker.internal;Database=dbl_progress;User Id=sa;Password=YourPassword;TrustServerCertificate=True" \
  -e "ConnectionStrings__umbracoDbDSN_ProviderName=Microsoft.Data.SqlClient" \
  progress-cu:dev

5. docker-compose for Local Development

The compose file (docker-compose.yml, aliased as docker-compose.local-test.yml) defines two services:

Service Container Port Purpose
mssql progress-test-sql 1436:1433 SQL Server 2022 Developer
umbraco progress-test-umbraco 5281:8080 Umbraco v17 website

Start the full stack

# From the repo root
docker compose up -d

# Or with explicit file
docker compose -f docker-compose.yml up -d

# Build and start (force rebuild)
docker compose up -d --build

Access points

  • Umbraco backoffice: http://localhost:5281/umbraco
  • SQL Server: localhost,1436 (user: sa, password: ProgressTest1!)

Database initialization

The compose mounts ./docker/init-db/ into the SQL container. The init-db.sql script creates two databases on first run:

  • dbl_progress_test — target v17 database
  • dbl_progress_source — source v8 database (restore from backup)

Stop and clean up

# Stop all services
docker compose down

# Stop and remove volumes (full reset)
docker compose down -v

# View logs
docker compose logs -f umbraco
docker compose logs -f mssql

6. Image Tagging Strategy

<registry>/progress-cu:<tag>
Tag Pattern Example Use
latest progress-cu:latest Most recent Release build
<semver> progress-cu:1.2.0 Specific release version
<client>-<semver> progress-cu:pcu-1.2.0 Client-specific build
dev progress-cu:dev Development/debug build
<branch>-<sha> progress-cu:main-a1b2c3d CI pipeline build

7. Azure Container Registry

Setup

# Login to ACR
az acr login --name <your-acr-name>

# Or with Docker directly
docker login <your-acr-name>.azurecr.io

Push

ACR=<your-acr-name>.azurecr.io

# Tag and push
docker tag progress-cu:latest $ACR/progress-cu:latest
docker tag progress-cu:latest $ACR/progress-cu:1.2.0
docker push $ACR/progress-cu:latest
docker push $ACR/progress-cu:1.2.0

Pull

docker pull $ACR/progress-cu:latest

CI pipeline (single command)

az acr build \
  --registry <your-acr-name> \
  --image progress-cu:1.2.0 \
  --build-arg CONFIGURATION=Release \
  --build-arg ASPNETCORE_ENVIRONMENT=Production \
  ./dbl.Progress

8. Environment Variables Reference

Variable Required Default Description
ASPNETCORE_ENVIRONMENT Yes Debug Development, Staging, Production
ASPNETCORE_HTTP_PORTS No 8080 HTTP listen port
ASPNETCORE_URLS No Override listen URLs (e.g., http://+:8080)
ConnectionStrings__umbracoDbDSN Yes SQL Server connection string
ConnectionStrings__umbracoDbDSN_ProviderName Yes Always Microsoft.Data.SqlClient
Umbraco__CMS__Unattended__InstallUnattended No false Auto-install schema on first run
Umbraco__CMS__Unattended__UpgradeUnattended No false Auto-upgrade schema
Umbraco__CMS__Unattended__UnattendedUserName No Admin username for unattended install
Umbraco__CMS__Unattended__UnattendedUserEmail No Admin email for unattended install
Umbraco__CMS__Unattended__UnattendedUserPassword No Admin password for unattended install
Umbraco__CMS__Runtime__Mode No Production BackofficeDevelopment, Development, Production
Umbraco__CMS__Global__UseHttps No true Set false behind a reverse proxy
Umbraco__CMS__ModelsBuilder__ModelsMode No InMemoryAuto Nothing, InMemoryAuto, SourceCodeManual

9. Volume Mounts

Volume Container Path Purpose
umbraco-media /app/wwwroot/media Uploaded media files
umbraco-temp /app/umbraco/Data Umbraco temp data, SQLite fallback
progress-test-sql-data /var/opt/mssql SQL Server data files

Production recommendations

# Named volumes for persistence
docker volume create progress-media
docker volume create progress-keys
docker volume create progress-logs

# Mount at run time
-v progress-media:/app/wwwroot/media
-v progress-keys:/app/DataProtection-Keys
-v progress-logs:/app/umbraco/Logs

Data protection keys must be persisted — otherwise authentication cookies are invalidated on every container restart.

10. Health Checks

SQL Server (in compose)

healthcheck:
  test: /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$$PASSWORD" -C -Q "SELECT 1" || exit 1
  interval: 10s
  timeout: 5s
  retries: 10
  start_period: 30s

Umbraco (add to compose or Dockerfile)

healthcheck:
  test: curl -f http://localhost:8080/umbraco/ping || exit 1
  interval: 30s
  timeout: 10s
  retries: 3
  start_period: 60s

Azure Container Instance / App Service

Use the same /umbraco/ping endpoint for liveness and readiness probes.

11. Troubleshooting

Container exits immediately

# Check logs
docker logs progress-test-umbraco

# Common cause: missing or wrong connection string
# Verify SQL is reachable from within the container
docker exec progress-test-umbraco bash -c "apt-get update && apt-get install -y iputils-ping && ping mssql"

"Login failed for user" in SQL

  • Verify SA_PASSWORD meets complexity requirements (uppercase, lowercase, digit, symbol, 8+ chars)
  • Check the SQL container is healthy: docker compose ps
  • Wait for SQL to finish starting (30s+ on first run)

Umbraco hangs on install

  • Ensure Umbraco__CMS__Unattended__InstallUnattended=true is set
  • Check SQL connectivity from the Umbraco container
  • Look for lock files in the umbraco-temp volume: docker exec progress-test-umbraco ls /app/umbraco/Data

Node.js build fails during Docker build

# The build stage installs Node 18. If npm ci fails:
# 1. Check package-lock.json is committed and up to date
# 2. Try building with --no-cache
docker build --no-cache -t progress-cu:dev ./dbl.Progress

Media files missing after restart

Mount a named volume for media:

-v progress-media:/app/wwwroot/media

Without this, media is lost when the container is recreated.

Port conflicts

The compose uses non-standard ports to avoid conflicts:

Service Host Port Change with
SQL 1436 Edit ports: in compose
Umbraco 5281 Edit ports: in compose

Full reset

# Nuclear option — removes everything
docker compose down -v
docker system prune -f
docker compose up -d --build
Migration documentation by Double for Progress Credit Union