Docker Deployment
AuthProxy uses a base image + binary mounting strategy for flexible, multi-tenant deployment. This approach enables easy versioning, rollback, and efficient storage.
Deployment Philosophy
DO NOT store container versions. Instead:
- Store binaries by semantic version
- Use single base image for all services
- Mount binaries at runtime via volumes
Benefits
✅ Easy Rollback - Just mount previous version ✅ Storage Efficient - One base image serves many projects ✅ Fast Deployment - No image build needed ✅ Version Flexibility - Each module independently versioned ✅ Multi-Tenant Ready - Same base image, different binaries per customer
Base Image
AuthProxy uses a minimal .NET 10 Alpine base image:
# itbuild/dotnet-base:10.0
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine
WORKDIR /app
# Health check
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:80/health || exit 1
ENTRYPOINT ["dotnet"]
Why Alpine?
- Minimal size (~100MB vs ~200MB for Debian)
- Reduced attack surface
- Faster container startup
Multi-Tenant Deployment
Each customer project has:
- Unique port (e.g., 8001-8999 for DEV)
- Separate VM or shared infrastructure
- Separate databases per module
- Isolated Docker network
File Structure
/projects/{projectId}/
├── configs/
│ ├── authproxy.json # AuthProxy configuration
│ ├── wallet.json # TrexWallet configuration
│ └── core.json # Core module configuration
├── AuthProxy/
│ ├── 1.0.0/ # Binaries + wwwroot
│ │ ├── AuthProxy.dll
│ │ ├── *.dll
│ │ └── wwwroot/ # PWA frontends
│ ├── 1.1.0/
│ └── 1.2.0/ # Latest version
├── TrexWallet/
│ ├── 1.0.0/
│ └── 1.1.0/
└── Core/
└── 1.0.0/
Deployment Examples
AuthProxy (Public-Facing)
AuthProxy needs host port mapping since it serves PWA frontends and is the entry point:
docker run -d \
--name project123-authproxy \
-p 8001:80 \
-v /projects/123/AuthProxy/1.2.0:/app:ro \
-v /projects/123/configs/authproxy.json:/app/appsettings.json:ro \
-e ASPNETCORE_ENVIRONMENT=Production \
--network project123_net \
itbuild/dotnet-base:10.0 \
AuthProxy.dll
Parameters Explained:
| Parameter | Purpose | Value |
|---|---|---|
--name | Container name | project{id}-authproxy |
-p 8001:80 | Host port mapping | DEV: 8001-8999, PROD: 80/443 |
-v .../1.2.0:/app:ro | Mount binaries (read-only) | Specific version |
-v .../authproxy.json:/app/appsettings.json:ro | Mount config | Customer-specific config |
--network | Docker network | Isolated per project |
AuthProxy.dll | Entry point | Passed to dotnet |
TrexWallet (Internal Service)
Internal services don't need host ports - they use Docker network:
docker run -d \
--name project123-wallet \
--network project123_net \
-v /projects/123/TrexWallet/1.0.0:/app:ro \
-v /projects/123/configs/wallet.json:/app/appsettings.json:ro \
-e ASPNETCORE_ENVIRONMENT=Production \
itbuild/dotnet-base:10.0 \
TrexWallet.dll
Key Differences:
- ❌ NO
-pflag (not exposed to host) - ✅ Only on Docker network
- ✅ Accessed via
http://project123-wallet:80
Core Module (Customer's Business Logic)
docker run -d \
--name project123-core \
--network project123_net \
-v /projects/123/Core/1.0.0:/app:ro \
-v /projects/123/configs/core.json:/app/appsettings.json:ro \
-e ASPNETCORE_ENVIRONMENT=Production \
itbuild/dotnet-base:10.0 \
Core.dll
Port Management
Shared Development Environment
- Port Range: 8001-8999 (per-customer projects on shared dev stand)
- Assignment: Automatic from database
- Edge forwarding: public customer port → project VM customer port
The assigned port is shown in the project administration UI and deployment logs.
DNS Configuration:
customer-domain:customer-port → edge gateway → project VM:customer-port
Production Environment
Option A: Direct Port 80/443
AuthProxy listens on the standard HTTP/HTTPS ports.
Option B: Behind Reverse Proxy
A reverse proxy terminates TLS and forwards traffic to AuthProxy on an internal container port.
AuthProxy Port Mapping
Host Mode (direct port exposure):
-p 8001:80 # Host 8001 → Container 80
Why Host Mode for AuthProxy?
- AuthProxy IS the reverse proxy
- Direct client access needed
- Serves static files (PWA frontends)
Internal Services
Network Mode (no host ports):
--network project123_net # No -p flag
Access:
http://project123-wallet:80/wallet/v1/balance
http://project123-chat:80/chat/v1/messages
http://project123-core:80/core/v1/api
Docker Compose
Complete Multi-Tenant Stack
version: '3.8'
services:
authproxy:
image: itbuild/dotnet-base:10.0
container_name: project123-authproxy
ports:
- "8001:80"
volumes:
- /projects/123/AuthProxy/1.2.0:/app:ro
- /projects/123/configs/authproxy.json:/app/appsettings.json:ro
environment:
- ASPNETCORE_ENVIRONMENT=Production
command: ["AuthProxy.dll"]
networks:
- project123_net
depends_on:
- sql-server
restart: unless-stopped
wallet:
image: itbuild/dotnet-base:10.0
container_name: project123-wallet
volumes:
- /projects/123/TrexWallet/1.0.0:/app:ro
- /projects/123/configs/wallet.json:/app/appsettings.json:ro
environment:
- ASPNETCORE_ENVIRONMENT=Production
command: ["TrexWallet.dll"]
networks:
- project123_net
depends_on:
- sql-server
restart: unless-stopped
core:
image: itbuild/dotnet-base:10.0
container_name: project123-core
volumes:
- /projects/123/Core/1.0.0:/app:ro
- /projects/123/configs/core.json:/app/appsettings.json:ro
environment:
- ASPNETCORE_ENVIRONMENT=Production
command: ["Core.dll"]
networks:
- project123_net
depends_on:
- sql-server
restart: unless-stopped
sql-server:
image: mcr.microsoft.com/mssql/server:2022-latest
container_name: project123-sql
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=SqlServerPass123!
volumes:
- sql_data:/var/opt/mssql
networks:
- project123_net
restart: unless-stopped
networks:
project123_net:
driver: bridge
volumes:
sql_data:
Versioning & Rollback
Semantic Versioning
Binaries stored by semantic version:
/projects/123/AuthProxy/
├── 1.0.0/
├── 1.1.0/
├── 1.2.0/ (current - stable)
├── 1.2.1/ (failed deployment)
└── 1.3.0/ (testing)
Rollback Procedure
1. Stop Current Container:
docker stop project123-authproxy
docker rm project123-authproxy
2. Deploy Previous Version:
docker run -d \
--name project123-authproxy \
-p 8001:80 \
-v /projects/123/AuthProxy/1.2.0:/app:ro \ # ← Previous version
-v /projects/123/configs/authproxy.json:/app/appsettings.json:ro \
--network project123_net \
itbuild/dotnet-base:10.0 \
AuthProxy.dll
3. Verify:
curl http://localhost:8001/health
Automatic Rollback Script
#!/bin/bash
# rollback.sh
PROJECT_ID=$1
SERVICE=$2
VERSION=$3
CONTAINER_NAME="project${PROJECT_ID}-${SERVICE}"
BINARY_PATH="/projects/${PROJECT_ID}/${SERVICE}/${VERSION}"
CONFIG_PATH="/projects/${PROJECT_ID}/configs/${SERVICE,,}.json"
# Stop and remove
docker stop ${CONTAINER_NAME}
docker rm ${CONTAINER_NAME}
# Redeploy
if [ "$SERVICE" = "AuthProxy" ]; then
# AuthProxy needs port
docker run -d \
--name ${CONTAINER_NAME} \
-p 8001:80 \
-v ${BINARY_PATH}:/app:ro \
-v ${CONFIG_PATH}:/app/appsettings.json:ro \
--network project${PROJECT_ID}_net \
itbuild/dotnet-base:10.0 \
${SERVICE}.dll
else
# Internal service
docker run -d \
--name ${CONTAINER_NAME} \
--network project${PROJECT_ID}_net \
-v ${BINARY_PATH}:/app:ro \
-v ${CONFIG_PATH}:/app/appsettings.json:ro \
itbuild/dotnet-base:10.0 \
${SERVICE}.dll
fi
echo "Rolled back ${SERVICE} to version ${VERSION}"
Usage:
./rollback.sh 123 AuthProxy 1.2.0
./rollback.sh 123 TrexWallet 1.0.0
Configuration
appsettings.json (Mounted)
Configuration is outside the container:
{
"ConnectionStrings": {
"DefaultConnection": "Server=project123-sql;Database=project123_authproxy;User Id=authproxy;Password=SecurePass123;Encrypt=true;"
},
"Config": {
"ServiceId": 10,
"Project": "CustomerProject",
"Domain": "app.customer.com",
"CoreApiKey": "generated_secure_key",
"Modules": {
"Wallet": "http://project123-wallet:80",
"Core": "http://project123-core:80"
}
},
"FIDO2": {
"serverDomain": "customer.com",
"serverName": "app.customer.com",
"origins": ["https://app.customer.com"]
},
"Uptrace": {
"Dsn": "http://uptrace-server:14318",
"ServiceName": "authproxy"
}
}
Why Mount Config?
- Update config without rebuilding
- Customer-specific settings
- Secrets management (can be encrypted)
Environment Variables
Override config via environment:
docker run -d \
-e ConnectionStrings__DefaultConnection="..." \
-e ServiceId="10" \
-e Config__Domain="app.customer.com" \
...
Secrets Management
Option 1: Docker Secrets
services:
authproxy:
secrets:
- db_password
- api_key
secrets:
db_password:
external: true
api_key:
external: true
Option 2: Encrypted in Config
Sensitive values can be encrypted in configuration:
{
"Config": {
"CoreApiKey": "encrypted:AES256:base64_encrypted_value"
}
}
Networking
Docker Network Per Project
Each project has isolated network:
docker network create project123_net
Services on Network:
project123-authproxy(8001:80)project123-wallet(80 internal)project123-core(80 internal)project123-sql(1433 internal)
Inter-Service Communication:
AuthProxy → http://project123-wallet:80/wallet/v1/balance
Wallet → http://project123-core:80/core/v1/users/123
External Access
DEV:
Internet → customer endpoint → edge gateway → Docker:80
PROD:
Internet → app.customer.com → Nginx/Caddy → Docker:8080
Monitoring & Health
Health Checks
All services expose /health endpoint:
curl http://localhost:8001/health
# Response: {"status": "healthy"}
Docker Health Check:
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:80/health || exit 1
Uptrace Integration
All containers send traces/logs to Uptrace:
services:
uptrace:
image: uptrace/uptrace:latest
ports:
- "14317:14317" # gRPC
- "14318:14318" # HTTP
volumes:
- uptrace_data:/var/lib/uptrace
volumes:
uptrace_data:
Container Logs
# View logs
docker logs -f project123-authproxy
# Last 100 lines
docker logs --tail 100 project123-authproxy
# Since timestamp
docker logs --since 2024-01-01T00:00:00 project123-authproxy
Production Considerations
Resource Limits
services:
authproxy:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '1.0'
memory: 1G
Restart Policies
restart: unless-stopped # Recommended for production
SSL/TLS
Option A: Terminate at Nginx
server {
listen 443 ssl http2;
server_name app.customer.com;
ssl_certificate /etc/ssl/certs/customer.crt;
ssl_certificate_key /etc/ssl/private/customer.key;
location / {
proxy_pass http://localhost:8001;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Option B: Kestrel HTTPS
docker run -d \
-p 443:443 \
-v /certs/customer.pfx:/app/certificate.pfx:ro \
-e ASPNETCORE_Kestrel__Certificates__Default__Path=/app/certificate.pfx \
-e ASPNETCORE_Kestrel__Certificates__Default__Password=CertPassword \
...
Backup & Disaster Recovery
Backup:
# Backup binaries
tar -czf authproxy-1.2.0.tar.gz /projects/123/AuthProxy/1.2.0
# Backup config
cp /projects/123/configs/*.json /backups/configs/
# Backup database
docker exec project123-sql /opt/mssql-tools/bin/sqlcmd \
-S localhost -U sa -P 'Password' \
-Q "BACKUP DATABASE project123_authproxy TO DISK='/var/opt/mssql/backup/authproxy.bak'"
Troubleshooting
Container Won't Start
Check logs:
docker logs project123-authproxy
Common issues:
- Port already in use: Change port or stop conflicting service
- Volume mount failed: Check path exists and has permissions
- Config error: Validate JSON syntax
- Database unreachable: Check network and connection string
Service Unreachable
Test from host:
curl http://localhost:8001/health
Test from another container:
docker exec -it project123-wallet wget -O- http://project123-authproxy:80/health
High Memory Usage
Check stats:
docker stats project123-authproxy
Set limits:
docker update --memory 2G project123-authproxy
Rollback Failed
Nuclear option (restore from backup):
# Stop all
docker-compose down
# Restore binaries
tar -xzf authproxy-1.2.0.tar.gz -C /
# Restore config
cp /backups/configs/*.json /projects/123/configs/
# Start
docker-compose up -d