Containerizing your ASP.NET Core Clean Architecture API and orchestrating it locally with Docker Compose is a huge milestone. However, the true test of any modern software pipeline is how smoothly it transitions from your local development machine into a scalable, highly available production cloud environment.

While Kubernetes offers incredible power, managing clusters often introduces unnecessary operational overhead for many enterprise teams. A much leaner, highly reliable approach is deploying containerized web applications directly to Azure App Service (Linux Containers). App Service manages the underlying infrastructure, operating system patches, load balancing, and scaling, while GitHub Actions handles your continuous integration and continuous deployment (CI/CD) automatically.

In this comprehensive guide, we will walk through setting up your Azure resources, configuring secure authentication secrets, and building a fully automated deployment pipeline.

The Architecture of Cloud Container Deployments

The deployment lifecycle follows a clean, automated sequence on every push to your main branch:

  1. Build & Test: GitHub Actions checks out your code, restores dependencies, builds the multi-project solution, and runs your unit and integration test suites.

  2. Container Registry Push: Verified builds are packaged via your Dockerfile, tagged with the unique Git commit SHA, and pushed securely to Azure Container Registry (ACR).

  3. Cloud Provisioning: GitHub Actions authenticates with Azure using a Service Principal and instructs Azure App Service to pull and spin up the newly updated container image.

Step 1: Provisioning Azure Prerequisites

Before configuring your pipeline, ensure you have two core resources set up in your Azure Portal:

  1. Azure Container Registry (ACR): A private Docker registry to store your compiled container images (e.g., myenterpriseacr.azurecr.io).

  2. Azure App Service (Linux): Created with the Docker Container publishing option, pointing to your ACR registry and specifying an initial startup container.

Step 2: Configuring GitHub Repository Secrets

To allow GitHub Actions to communicate securely with your Azure subscription and push images to ACR without exposing passwords in plain text, you need to configure Repository Secrets.

  1. Generate an Azure Service Principal with Contributor permissions using the Azure CLI:

    Bash

    az ad sp create-for-rbac --name "GitHubActionsDeploymentSP" --role Contributor --scopes /subscriptions/{your-subscription-id}/resourceGroups/{your-resource-group} --sdk-auth
    
  2. Copy the resulting JSON output.

  3. In your GitHub repository, navigate to Settings > Secrets and variables > Actions, click New repository secret, name it AZURE_CREDENTIALS, and paste the JSON blob.

  4. Add three additional secrets required for container authentication and deployment:

    • REGISTRY_USERNAME: Your ACR username (or Service Principal Client ID).

    • REGISTRY_PASSWORD: Your ACR access key/password.

    • WEB_APP_NAME: The exact name of your Azure App Service.

Step 3: Writing the End-to-End CI/CD Pipeline

Now, update your GitHub Actions workflow file at .github/workflows/ci-cd.yml to handle testing, container building, ACR pushing, and App Service deployment in a single cohesive pipeline.

YAML

name: CI/CD to Azure App Service

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

env:
  REGISTRY_LOGIN_SERVER: myenterpriseacr.azurecr.io
  IMAGE_NAME: enterprise-api

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout Code
      uses: actions/checkout@v4

    - name: Set up .NET Core SDK
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: '8.0.x'

    - name: Restore Dependencies
      run: dotnet restore

    - name: Build Solution
      run: dotnet build --no-restore --configuration Release

    - name: Run Unit & Integration Tests
      run: dotnet test --no-build --verbosity normal --configuration Release

  docker-deploy:
    needs: build-and-test
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest

    steps:
    - name: Checkout Code
      uses: actions/checkout@v4

    - name: Log in to Azure Container Registry
      uses: azure/docker-login@v1
      with:
        login-server: ${{ env.REGISTRY_LOGIN_SERVER }}
        username: ${{ secrets.REGISTRY_USERNAME }}
        password: ${{ secrets.REGISTRY_PASSWORD }}

    - name: Build and Push Docker Image to ACR
      uses: docker/build-push-action@v5
      with:
        context: .
        file: ./Dockerfile
        push: true
        tags: ${{ env.REGISTRY_LOGIN_SERVER }}/${{ env.IMAGE_NAME }}:${{ github.sha }}

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

    - name: Deploy Container to Azure App Service
      uses: azure/webapps-deploy@v2
      with:
        app-name: ${{ secrets.WEB_APP_NAME }}
        images: '${{ env.REGISTRY_LOGIN_SERVER }}/${{ env.IMAGE_NAME }}:${{ github.sha }}'

Step 4: Configuring Production Environment Settings in Azure

Never hardcode database connection strings, JWT secrets, or API keys into your application code. Once your container is deployed to Azure App Service, configure its runtime environment variables directly through the portal:

  1. Open your App Service in the Azure Portal.

  2. Navigate to Settings > Configuration.

  3. Under Application settings, add your production keys (e.g., ConnectionStrings:DefaultConnection, Jwt__Key, Jwt__Issuer, and ASPNETCORE_ENVIRONMENT=Production).

  4. Click Save. App Service will automatically restart your container with the secure production settings applied.

Summary

By connecting your Clean Architecture ASP.NET Core solution to Azure App Service via GitHub Actions, you achieve a professional, automated deployment pipeline:

  • Confidence: Code is thoroughly tested via automated xUnit test suites before any deployment begins.

  • Traceability: Every container image is tagged with the exact git commit SHA (${{ github.sha }}), making rollbacks trivial if ever needed.

  • Scalability: Your application runs in a fully managed, secure cloud environment ready for enterprise traffic.