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:
- Copy
.csprojfiles first, rundotnet restore(cached unless project refs change) - Copy
package.json/package-lock.json, runnpm ci(cached unless deps change) - 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 databasedbl_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¶
| 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¶
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_PASSWORDmeets 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=trueis set - Check SQL connectivity from the Umbraco container
- Look for lock files in the
umbraco-tempvolume: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:
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 |