100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
.NET

CI/CD for .NET Apps

How to build automated build, test, and deployment pipelines for .NET applications using GitHub Actions, the dotnet CLI, and containerized deployments.

Practical .NET CoreIntermediate10 min readJul 10, 2026
Analogies

What CI/CD Means for a .NET Project

Continuous Integration (CI) means every push or pull request automatically triggers a pipeline that restores dependencies, builds the solution, and runs the test suite, catching integration problems within minutes instead of at the next manual QA pass. Continuous Delivery/Deployment (CD) extends this by automatically packaging and shipping a build artifact — a self-contained executable, a NuGet package, or a Docker image — to a staging or production environment once it passes all quality gates. For .NET projects this is commonly implemented with GitHub Actions or Azure Pipelines, both of which have first-class setup-dotnet steps and can run on Windows, Linux, or macOS runners since .NET is cross-platform.

🏏

Cricket analogy: CI is like the third umpire reviewing every close call in real time during a match rather than only checking decisions at the end of the series — problems get caught and corrected within minutes, not weeks.

Building and Testing with the dotnet CLI in a Pipeline

A typical GitHub Actions workflow for a .NET app chains together dotnet restore, dotnet build --configuration Release --no-restore, and dotnet test --no-build --logger trx, with each step failing fast if the previous one didn't succeed. Caching the NuGet packages directory (~/.nuget/packages) keyed on the hash of the .csproj/packages.lock.json files can cut restore time dramatically on repeat runs. Test results in .trx format can be published as a check on the pull request, and dotnet publish -c Release -o ./publish produces the final deployable artifact, which the workflow uploads using actions/upload-artifact for the deployment job to consume.

🏏

Cricket analogy: Caching NuGet packages between runs is like a team keeping its match-day kit bag pre-packed rather than repacking every item from a warehouse before each fixture — most items don't change, so you skip the redundant work.

Deployment Strategies and Containerization

Most modern .NET deployments package the app as a Docker image using a multi-stage Dockerfile — one stage with the full SDK to build and publish, and a slim aspnet runtime-only stage that copies just the published output, keeping the final image small. Deployment strategies layered on top of this include blue-green deployment (routing traffic to a fully new environment only after it's verified healthy) and canary releases (shifting a small percentage of traffic to the new version before a full rollout), both of which reduce the blast radius of a bad release. Environment-specific configuration is handled through appsettings.{Environment}.json files combined with environment variables injected at deploy time, keeping secrets out of source control via tools like Azure Key Vault or GitHub Actions secrets.

🏏

Cricket analogy: A canary release is like a franchise trying out a promising young player in a handful of low-stakes T20 matches before committing them to the full Test squad for an entire series.

yaml
name: dotnet-ci-cd
on:
  push:
    branches: [ main ]
jobs:
  build-test-publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'
      - run: dotnet restore
      - run: dotnet build --configuration Release --no-restore
      - run: dotnet test --no-build --configuration Release --logger trx
      - run: dotnet publish -c Release -o ./publish
      - uses: actions/upload-artifact@v4
        with:
          name: app-build
          path: ./publish

Multi-stage Dockerfiles keep production images small and secure. Use mcr.microsoft.com/dotnet/sdk:8.0 for the build stage and mcr.microsoft.com/dotnet/aspnet:8.0 for the final runtime stage — the runtime image excludes the SDK and compiler tooling, shrinking the attack surface and image size significantly.

Never bake secrets like connection strings or API keys directly into appsettings.json committed to source control. Use GitHub Actions secrets, Azure Key Vault, or environment variables injected at container runtime, and add a .gitignore entry for any local override file such as appsettings.Development.local.json.

  • CI automatically restores, builds, and tests every push; CD automatically packages and deploys passing builds.
  • GitHub Actions and Azure Pipelines both provide first-class dotnet CLI support via setup-dotnet.
  • Cache the NuGet packages directory keyed on the lock file hash to speed up repeat builds.
  • dotnet publish produces the deployable artifact consumed by the deployment stage of the pipeline.
  • Multi-stage Dockerfiles separate the SDK build stage from a slim runtime-only final image.
  • Blue-green and canary strategies reduce the risk of a bad release reaching all users at once.
  • Keep secrets out of source control using GitHub Actions secrets or Azure Key Vault, injected at deploy time.

Practice what you learned

Was this page helpful?

Topics covered

#NET#NETCoreStudyNotes#MicrosoftTechnologies#CICDForNETApps#Apps#Means#Project#Building#DevOps#StudyNotes#SkillVeris