Documentation

how-to/deploy.md

How to Deploy Dynaplex

This guide covers deploying Dynaplex applications using .NET Aspire to various environments.

Deployment Options

  1. Azure Container Apps (Recommended) - Managed containers with Aspire support
  2. Docker Compose - Self-hosted containers
  3. Kubernetes - Advanced orchestration
  4. Azure App Service - Traditional PaaS

.NET Aspire has first-class support for Azure Container Apps.

Prerequisites

# Install Azure CLI
brew install azure-cli  # macOS
# or download from https://aka.ms/installazurecliwindows

# Install Aspire workload
dotnet workload install aspire

# Login to Azure
az login

Deploy with Aspire

# Navigate to AppHost project
cd projects/bbu-rfid/src/Acsis.Dynaplex.Projects.BbuRfid/

# Deploy to Azure
azd init
azd up

# Follow prompts to:
# 1. Choose subscription
# 2. Select region
# 3. Name your deployment

Aspire automatically:

  • Creates Azure Container Apps for each service
  • Provisions PostgreSQL databases
  • Configures service-to-service communication
  • Sets up Application Insights
  • Configures health checks

Access Your Deployment

# Get deployment URL
azd env get-values

# View logs
az containerapp logs show --name core-data --resource-group <your-rg>

# Monitor in Azure Portal
open https://portal.azure.com

Docker Deployment

Build Docker Images

Each service can be containerized:

Dockerfile for a component service:

# engines/core-data/Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src

# Copy project files
COPY ["engines/core-data/src/Acsis.Dynaplex.Engines.CoreData/", "engines/core-data/src/Acsis.Dynaplex.Engines.CoreData/"]
COPY ["engines/core-data/src/Acsis.Dynaplex.Engines.CoreData.Abstractions/", "engines/core-data/src/Acsis.Dynaplex.Engines.CoreData.Abstractions/"]
COPY ["Directory.Build.props", "./"]

# Restore and build
RUN dotnet restore "engines/core-data/src/Acsis.Dynaplex.Engines.CoreData/Acsis.Dynaplex.Engines.CoreData.csproj"
RUN dotnet build "engines/core-data/src/Acsis.Dynaplex.Engines.CoreData/Acsis.Dynaplex.Engines.CoreData.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "engines/core-data/src/Acsis.Dynaplex.Engines.CoreData/Acsis.Dynaplex.Engines.CoreData.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Acsis.Dynaplex.Engines.CoreData.dll"]

Docker Compose

docker-compose.yml:

version: '3.8'

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: acsis
    volumes:
      - postgres-data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  core-data:
    build:
      context: .
      dockerfile: engines/core-data/Dockerfile
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ConnectionStrings__DefaultConnection=Host=postgres;Database=acsis;Username=postgres;Password=${POSTGRES_PASSWORD}
    ports:
      - "40443:443"
    depends_on:
      - postgres

  identity:
    build:
      context: .
      dockerfile: engines/identity/Dockerfile
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ConnectionStrings__DefaultConnection=Host=postgres;Database=acsis;Username=postgres;Password=${POSTGRES_PASSWORD}
      - CoreDataApiUrl=https://core-data
    ports:
      - "41443:443"
    depends_on:
      - core-data

volumes:
  postgres-data:

Deploy:

# Build images
docker-compose build

# Start services
docker-compose up -d

# View logs
docker-compose logs -f

# Stop services
docker-compose down

Kubernetes Deployment

Generate Kubernetes Manifests

Aspire can generate K8s manifests:

# Install Aspire manifest tool
dotnet tool install -g aspirate

# Generate manifests
cd projects/bbu-rfid/src/Acsis.Dynaplex.Projects.BbuRfid/
aspirate generate

Deploy to Kubernetes

# Apply manifests
kubectl apply -f aspir8-output/

# Check deployment
kubectl get pods
kubectl get services

# View logs
kubectl logs -f deployment/core-data

# Port forward to access locally
kubectl port-forward service/core-data 40443:443

CI/CD Pipelines

GitHub Actions

.github/workflows/deploy.yml:

name: Deploy to Azure

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '9.0.x'

      - name: Install Aspire workload
        run: dotnet workload install aspire

      - name: Azure Login
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Deploy to Azure
        run: |
          cd projects/bbu-rfid/src/Acsis.Dynaplex.Projects.BbuRfid/
          azd deploy --no-prompt
        env:
          AZURE_ENV_NAME: ${{ secrets.AZURE_ENV_NAME }}
          AZURE_LOCATION: ${{ secrets.AZURE_LOCATION }}
          AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

Azure DevOps

azure-pipelines.yml:

trigger:
  branches:
    include:
      - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: UseDotNet@2
    inputs:
      packageType: 'sdk'
      version: '9.0.x'

  - script: dotnet workload install aspire
    displayName: 'Install Aspire workload'

  - task: AzureCLI@2
    inputs:
      azureSubscription: 'your-service-connection'
      scriptType: 'bash'
      scriptLocation: 'inlineScript'
      inlineScript: |
        cd projects/bbu-rfid/src/Acsis.Dynaplex.Projects.BbuRfid/
        azd deploy --no-prompt

Environment Configuration

Development

// appsettings.Development.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Host=localhost;Database=acsis;Username=postgres;Password=dev"
  }
}

Production

// appsettings.Production.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "Microsoft.AspNetCore": "Error"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "${CONNECTIONSTRING}" // Injected by Azure
  }
}

Secrets Management

Azure Key Vault:

# Create Key Vault
az keyvault create --name acsis-kv --resource-group acsis-rg

# Add secrets
az keyvault secret set --vault-name acsis-kv --name "ConnectionString" --value "your-connection-string"

# Grant access to managed identity
az keyvault set-policy --name acsis-kv \
  --object-id <managed-identity-id> \
  --secret-permissions get list

In code:

// Program.cs
builder.Configuration.AddAzureKeyVault(
    new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
    new DefaultAzureCredential());

Monitoring & Observability

Application Insights

Aspire automatically configures Application Insights:

// Already configured by Aspire ServiceDefaults
builder.AddServiceDefaults();

View Telemetry

# View in Azure Portal
open https://portal.azure.com

# Navigate to: Application Insights > your-app > Logs

# Query example
requests
| where timestamp > ago(1h)
| summarize count() by name
| order by count_ desc

Health Checks

# Check service health
curl https://your-app.azurecontainerapps.io/health

# View in Aspire Dashboard
curl https://your-app.azurecontainerapps.io/health/ready

Scaling

Azure Container Apps

# Scale manually
az containerapp update \
  --name core-data \
  --resource-group acsis-rg \
  --min-replicas 2 \
  --max-replicas 10

# Auto-scale based on HTTP requests
az containerapp update \
  --name core-data \
  --resource-group acsis-rg \
  --scale-rule-name http-scale \
  --scale-rule-http-concurrency 50

Kubernetes

# Scale deployment
kubectl scale deployment core-data --replicas=3

# Auto-scale
kubectl autoscale deployment core-data \
  --min=2 --max=10 \
  --cpu-percent=70

Rollback

Azure Container Apps

# List revisions
az containerapp revision list \
  --name core-data \
  --resource-group acsis-rg

# Activate previous revision
az containerapp revision activate \
  --name core-data \
  --resource-group acsis-rg \
  --revision <previous-revision-name>

Kubernetes

# Rollback deployment
kubectl rollout undo deployment/core-data

# Check rollout status
kubectl rollout status deployment/core-data

Troubleshooting

View Logs

Azure Container Apps:

az containerapp logs show \
  --name core-data \
  --resource-group acsis-rg \
  --follow

Docker:

docker logs -f core-data

Kubernetes:

kubectl logs -f deployment/core-data

Common Issues

Service can't reach database:

  • Check connection strings
  • Verify database exists
  • Check network security rules

Service returns 503:

  • Check health endpoint: /health
  • Verify dependencies are running
  • Check Application Insights for errors

High memory usage:

  • Review telemetry in Application Insights
  • Check for memory leaks
  • Scale horizontally

Best Practices

Use Aspire for Azure Container Apps deployment
Enable health checks on all services
Use managed identities for Azure resources
Store secrets in Azure Key Vault
Enable Application Insights for all services
Use horizontal scaling for high availability
Test deployments in staging environment first
Implement rollback strategy
Monitor costs in Azure Portal

Next Steps

References