Skip to main content

On This Page

Automating .NET CI/CD: A Guide to GitHub Actions and Azure Deployment

3 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

GitHub Actions for .NET: Build, Test, and Deploy Your API

GitHub Actions provides a native CI/CD ecosystem for .NET developers to automate the path from git push to Azure deployment. Implementing NuGet caching in these workflows can reduce build times by 30–90 seconds per run by avoiding fresh dependency downloads.

Why This Matters

Manual deployment processes introduce significant human risk and lack reproducibility, often failing under pressure or when steps are skipped. By transitioning to automated YAML workflows, teams ensure every commit is tested and built in a standardized environment, moving from fragile manual file copying to a robust, containerized deployment model that supports parallel testing and manual approval gates.

Key Insights

  • NuGet package caching uses the hash of .csproj files as a key to prevent redundant downloads in GitHub Actions runners.
  • Matrix builds enable parallel testing across multiple SDK versions, such as .NET 8.0.x and 9.0.x, to ensure library compatibility.
  • Docker multi-stage builds optimize production images by separating the build environment from the lightweight ASP.NET runtime.
  • GitHub Secrets and Environment variables allow for safe management of sensitive connection strings and API keys without hardcoding them.
  • Deployment jobs using azure/webapps-deploy@v3 can be restricted to specific branches and require manual environment approvals for production safety.

Working Examples

Standard .NET CI workflow for building and testing on every push or pull request.

name: CI
on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: '8.0.x'
    - name: Restore dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --no-restore --configuration Release
    - name: Test
      run: dotnet test --no-build --configuration Release --verbosity normal

Multi-stage Dockerfile for an ASP.NET Core 8.0 API.

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 8080
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApi/MyApi.csproj", "MyApi/"]
RUN dotnet restore "MyApi/MyApi.csproj"
COPY . .
WORKDIR "/src/MyApi"
RUN dotnet build --configuration Release --output /app/build
FROM build AS publish
RUN dotnet publish --configuration Release --output /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]

Practical Applications

  • System: Azure App Service deployment. Behavior: Using environment-specific manual approval gates ensures production deployments only occur after stakeholder review. Pitfall: Omitting the ‘needs’ keyword in workflow jobs, which allows deployment to proceed even if unit tests fail.
  • System: Docker Container Registry. Behavior: Tagging images with the GitHub commit SHA (github.sha) provides a unique immutable identifier for every build. Pitfall: Relying solely on the ‘latest’ tag, which makes rollbacks difficult and creates a moving target for production environments.

References:

Continue reading

Next article

Harness Engineering: Why Scaffolding Outperforms AI Models in 2026

Related Content