Breaking / LiveLIVE
DEV CommunityBear UI 1.3.5 — charts, AI, code, maps, and kanban now have their own packagesWIREDOil Spills Wreak Havoc on the Bottom of the Ocean, Even a Decade LaterHacker NewsFrance asked ISPs to prepare temporary DNS blocks for major social platformsGoogle DevelopersBring multimodal semantic search to the edge with EmbeddingGemma 2The Hacker NewsOpenAI AI Agents Demonstrate VM Escapes and Secret Cross-Sandbox CommunicationInfoQPingCAP Breach Exposes Customer Records and Source CodeThe VergeBlue Planet III is keeping the rough edges in the age of AITechCrunchStarlink seems to be working normally again in UkraineBleepingComputerBlackX UEFI bootkit exploits Baton Drop flaw to bypass Secure BootArs TechnicaA 1,000-mile cloud on Mars may reveal physics never seen in natureGitHub ChangelogUsage metrics API adds usage from tools like OpenCodeMicrosoft Power PlatformAI that understands your business: Dataverse and Work IQ at PPCC 2026AWS What's NewWeb Search on Amazon Bedrock now supports domain filtering and user location controlsMIT News: AIAt MIT, Martha Minow speaks on sustaining democracy, both by using and overcoming AIDEV CommunityBear UI 1.3.5 — charts, AI, code, maps, and kanban now have their own packagesWIREDOil Spills Wreak Havoc on the Bottom of the Ocean, Even a Decade LaterHacker NewsFrance asked ISPs to prepare temporary DNS blocks for major social platformsGoogle DevelopersBring multimodal semantic search to the edge with EmbeddingGemma 2The Hacker NewsOpenAI AI Agents Demonstrate VM Escapes and Secret Cross-Sandbox CommunicationInfoQPingCAP Breach Exposes Customer Records and Source CodeThe VergeBlue Planet III is keeping the rough edges in the age of AITechCrunchStarlink seems to be working normally again in UkraineBleepingComputerBlackX UEFI bootkit exploits Baton Drop flaw to bypass Secure BootArs TechnicaA 1,000-mile cloud on Mars may reveal physics never seen in natureGitHub ChangelogUsage metrics API adds usage from tools like OpenCodeMicrosoft Power PlatformAI that understands your business: Dataverse and Work IQ at PPCC 2026AWS What's NewWeb Search on Amazon Bedrock now supports domain filtering and user location controlsMIT News: AIAt MIT, Martha Minow speaks on sustaining democracy, both by using and overcoming AI

CI/CD for .NET with GitHub Actions: Build, Test and Deploy Automatically

A practical GitHub Actions pipeline for ASP.NET Core: restore, build, test, publish, and deploy safely with secrets and environment approvals.

By KHR Editorial Team··3 min read
CI/CD for .NET with GitHub Actions: Build, Test and Deploy Automatically

Manual deployments are slow and risky. Someone builds on their own machine, copies files to a server and hopes nothing was missed. A CI/CD pipeline makes every change go through the same steps: build, test, package and deploy. GitHub Actions is a popular choice for .NET teams because it lives next to the code and is easy to start with.

The goal

  1. Every push and pull request is built and tested automatically.
  2. Merges to the main branch produce a versioned artifact.
  3. Deployments to production happen from that artifact, with approval and secrets kept safe.

A basic CI workflow

Create .github/workflows/ci.yml:

name: ci

on:
  push:
    branches: [main]
  pull_request:

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

      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 8.0.x
          cache: true
          cache-dependency-path: "**/packages.lock.json"

      - run: dotnet restore --locked-mode
      - run: dotnet build --no-restore -c Release
      - run: dotnet test --no-build -c Release --logger trx --results-directory TestResults

      - run: dotnet publish src/Shop.Api -c Release -o publish --no-build
      - uses: actions/upload-artifact@v4
        with:
          name: api
          path: publish

A few choices worth explaining:

  • --locked-mode with a packages.lock.json makes restores reproducible.
  • --no-restore and --no-build avoid repeating work in later steps.
  • Artifacts let the deploy job use exactly what was tested.

Running tests that need a database

Integration tests often need SQL Server or PostgreSQL. GitHub Actions supports service containers:

    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: postgres
        ports: ["5432:5432"]

Your tests connect to localhost:5432. Alternatively, use Testcontainers inside the tests themselves.

Deploying with environments and approvals

Add a deploy job that runs only on the main branch and uses a GitHub environment:

  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: api
          path: publish
      - name: Deploy
        run: ./deploy.sh
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

In repository settings, configure the production environment with required reviewers. The job then waits for an approved person before deploying. Secrets defined on the environment are only available to jobs that use it.

Where to deploy

  • Azure App Service: use the official Azure login and web app deploy actions, preferably with OpenID Connect instead of long-lived passwords.
  • Containers: build and push a Docker image to a registry, then update your container service or Kubernetes deployment.
  • Your own Windows or Linux server: use a self-hosted runner inside your network or a secure deployment script over SSH.

Database migrations

Decide how schema changes reach production. Common options:

  • Generate an idempotent SQL script with dotnet ef migrations script --idempotent and apply it as a deploy step.
  • Use EF Core migration bundles (dotnet ef migrations bundle) for a single executable that applies migrations.

Avoid applying migrations automatically at application startup in production for anything beyond small apps, because failures become harder to control.

Good practices

Practice Why
Require CI to pass before merging Broken code never reaches main
Keep secrets in GitHub secrets or OIDC No passwords in the repository
Version artifacts with the commit SHA Easy rollback and traceability
Use environment approvals for production A human confirms risky changes
Cache NuGet packages Faster pipelines
Run security scanning Catch vulnerable dependencies early

Frequently asked questions

GitHub Actions or GitLab CI? Both work very well for .NET. Use the platform where your code already lives. The concepts are the same: build, test, artifact, and protected deployment.

How long should a pipeline take? Aim for under ten minutes for pull request checks so developers get fast feedback. Use caching, run slow tests separately, and parallelise where possible.

Can we deploy to an on-premise IIS server? Yes. A self-hosted runner inside your network can stop the site or app pool, copy the published files and start it again, or you can use Web Deploy. Keep credentials in the CI secret store.

How do we roll back a bad release? Redeploy the previous artifact, which is easy when every build is versioned and stored. For database changes, write migrations that are backward compatible so the previous version can still run.

Key takeaways

  • Build and test every push and pull request, and publish one artifact per change.
  • Deploy that same artifact using protected environments with approvals and scoped secrets.
  • Plan database migrations as an explicit deploy step.
  • Start simple, then add caching, scanning and container builds as the project grows.

KHR Digital Solutions sets up CI/CD pipelines on GitHub and GitLab for .NET and Next.js projects, including deployments to cloud and on-premise servers.

CI/CD for .NET with GitHub Actions: Build, Test and Deploy Automatical | KHR Tech Blog