.NET has changed a lot over the past few years — cross-platform .NET Core (now just “.NET”) means you’re no longer locked into Windows-only build servers, and Jenkins has kept pace as a solid CI/CD option for .NET teams who don’t want to be tied entirely to Azure DevOps. Whether you’re building an ASP.NET Core web API, a Blazor app, or a legacy .NET Framework project, Jenkins can handle the full build-test-deploy cycle.
This guide covers setting up Jenkins for .NET from scratch, on both Windows and Linux agents, with complete pipeline examples.
Why Jenkins for .NET Projects?
Teams often assume .NET means Azure DevOps or GitHub Actions by default, but Jenkins offers a few real advantages: it’s platform-agnostic (works identically whether your target is Windows or Linux containers), it’s free and self-hosted so you control the infrastructure, and it integrates with virtually every other tool in a typical DevOps toolchain — SonarQube, Artifactory, Kubernetes, whatever you’re already using elsewhere.
Jenkins Architecture for .NET Builds
Since .NET (5/6/7/8+) is cross-platform, you have a choice: run your Jenkins agents on Windows with the full .NET SDK, or on Linux using the cross-platform SDK and Docker. Legacy .NET Framework projects (4.x) still require a Windows agent since the Framework itself is Windows-only. Modern .NET projects targeting net8.0 or similar can build and even run on Linux agents, which is generally cheaper and easier to containerize.
Prerequisites
- Jenkins server installed and running
- .NET SDK installed on your build agent (matching your project’s target framework)
- Git installed on the agent
- Your .NET project in a Git repository with a
.slnor.csprojfile
Step 1: Install the .NET SDK on Your Jenkins Agent
On a Linux agent (Ubuntu example):
wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt-get update
sudo apt-get install -y dotnet-sdk-8.0
Verify:
dotnet --version
On a Windows agent, download and run the installer from the official .NET download page, or use Chocolatey:
choco install dotnet-8.0-sdk -y
Step 2: Install Relevant Jenkins Plugins
In Manage Jenkins > Plugins, install:
- MSBuild Plugin — useful if you’re building
.slnfiles with MSBuild directly (mainly for .NET Framework) - NUnit Plugin or xUnit Plugin — to parse and display .NET test results in Jenkins
- Git Plugin — for source checkout
- Pipeline — for Jenkinsfile-based builds (should be installed by default)
Step 3: Configure the .NET SDK as a Jenkins Tool (Optional)
If you want Jenkins to manage the SDK path explicitly rather than relying on it being in PATH, go to Manage Jenkins > Tools and add a custom tool pointing at your dotnet installation directory. Many teams skip this and simply ensure dotnet is available in PATH on the agent, which is simpler to maintain.
Step 4: Basic Freestyle Job (Quick Start)
For teams just getting started, a freestyle job with a simple shell/batch build step works:
Linux agent build step:
dotnet restore
dotnet build --configuration Release
dotnet test --configuration Release --logger "trx;LogFileName=test-results.trx"
Windows agent build step (batch):
dotnet restore
dotnet build --configuration Release
dotnet test --configuration Release --logger "trx;LogFileName=test-results.trx"
But for anything beyond a quick prototype, a declarative Jenkinsfile pipeline is the better long-term approach.
Step 5: Complete Jenkinsfile for a .NET Web API
pipeline {
agent any
environment {
DOTNET_CLI_TELEMETRY_OPTOUT = '1'
DOTNET_NOLOGO = 'true'
BUILD_CONFIGURATION = 'Release'
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/yourorg/dotnet-api.git'
}
}
stage('Restore') {
steps {
sh 'dotnet restore'
}
}
stage('Build') {
steps {
sh "dotnet build --configuration ${BUILD_CONFIGURATION} --no-restore"
}
}
stage('Unit Tests') {
steps {
sh """
dotnet test --configuration ${BUILD_CONFIGURATION} \
--no-build \
--logger "trx;LogFileName=test-results.trx" \
--results-directory ./TestResults
"""
}
post {
always {
mstest testResultsFile: '**/TestResults/*.trx'
}
}
}
stage('Publish') {
steps {
sh """
dotnet publish --configuration ${BUILD_CONFIGURATION} \
--no-build \
--output ./publish
"""
}
}
stage('Archive Artifacts') {
steps {
archiveArtifacts artifacts: 'publish/**', fingerprint: true
}
}
stage('Docker Build') {
steps {
sh "docker build -t my-dotnet-api:${BUILD_NUMBER} ."
}
}
}
post {
always {
cleanWs()
}
success {
echo 'Build and tests passed.'
}
failure {
echo 'Build or tests failed - check logs above.'
}
}
}
Step 6: A Sample Dockerfile for Deployment
Since most modern .NET deployments end up containerized, here’s a typical multi-stage Dockerfile that pairs well with the pipeline above:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
Step 7: Deploying to Azure App Service (Common .NET Target)
Many .NET teams deploy to Azure. Here’s a pipeline stage using the Azure CLI:
stage('Deploy to Azure App Service') {
steps {
withCredentials([usernamePassword(credentialsId: 'azure-sp', usernameVariable: 'AZURE_CLIENT_ID', passwordVariable: 'AZURE_CLIENT_SECRET')]) {
sh """
az login --service-principal -u \$AZURE_CLIENT_ID -p \$AZURE_CLIENT_SECRET --tenant your-tenant-id
az webapp deploy --resource-group my-rg --name my-dotnet-app --src-path ./publish.zip
"""
}
}
}
Working with .NET Framework (Legacy) Projects
If you’re stuck maintaining an older .NET Framework 4.x project, you need a Windows agent with MSBuild and NuGet installed:
pipeline {
agent { label 'windows' }
stages {
stage('Restore NuGet Packages') {
steps {
bat 'nuget restore MySolution.sln'
}
}
stage('Build with MSBuild') {
steps {
bat '"C:\\Program Files\\Microsoft Visual Studio\\2022\\BuildTools\\MSBuild\\Current\\Bin\\MSBuild.exe" MySolution.sln /p:Configuration=Release'
}
}
stage('Run Tests') {
steps {
bat 'vstest.console.exe MyTests\\bin\\Release\\MyTests.dll'
}
}
}
}
Integrating with SonarQube for .NET
SonarQube has a dedicated .NET scanner that wraps the build:
stage('SonarQube Analysis') {
steps {
withSonarQubeEnv('SonarQube') {
sh '''
dotnet sonarscanner begin /k:"my-dotnet-app" /d:sonar.host.url="http://sonarqube:9000"
dotnet build --configuration Release
dotnet sonarscanner end
'''
}
}
}
Multi-Branch Pipelines for .NET
Most .NET teams work with feature branches and pull requests. Set up a Multibranch Pipeline job in Jenkins pointing at your repository, and Jenkins will automatically discover branches and PRs, running the Jenkinsfile for each. Combine this with branch-specific stages:
stage('Deploy to Staging') {
when {
branch 'develop'
}
steps {
echo 'Deploying to staging environment...'
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
steps {
input message: 'Deploy to production?'
echo 'Deploying to production...'
}
}
Troubleshooting
dotnet: command not found: The SDK isn’t installed or isn’t in PATH on the agent — verify with which dotnet (Linux) or where dotnet (Windows).
NuGet restore fails behind a corporate proxy: Configure a NuGet.Config with proxy settings, or set the HTTP_PROXY/HTTPS_PROXY environment variables on the agent.
Test results don’t show in Jenkins: Confirm the xUnit/NUnit/MSTest plugin is installed and the post-build step’s file pattern actually matches where .trx files are written.
MSBuild not found on Windows agent: Install Visual Studio Build Tools and reference the exact path to MSBuild.exe, which varies by VS version.
Security Best Practices
- Store NuGet feed credentials and Azure service principal secrets in Jenkins Credentials, never in
NuGet.Configcommitted to source control - Use
dotnet list package --vulnerablein a pipeline stage to catch known-vulnerable NuGet packages - Run builds with least-privilege service accounts, especially on Windows agents
- Keep the .NET SDK on agents updated to receive security patches
FAQs
Do I need a Windows agent for all .NET projects? No — only for legacy .NET Framework projects. Modern .NET (5 and later) is cross-platform and builds fine on Linux agents, which are usually cheaper to run.
Can Jenkins run my .NET unit tests and show results in the UI? Yes, using the xUnit or MSTest plugin to parse .trx or JUnit-format test result files generated by dotnet test.
How do I handle NuGet package caching to speed up builds? Mount a persistent volume or agent-local directory for the NuGet package cache (~/.nuget/packages) so restores don’t re-download unchanged packages every build.
Can I build a .NET MAUI or Blazor WebAssembly app in Jenkins? Yes, both build using the standard dotnet build/dotnet publish commands, though MAUI mobile targets require the relevant platform SDKs (Android SDK, Xcode for iOS) on the agent.
Is Jenkins a good alternative to Azure DevOps for .NET teams? It’s a legitimate option, especially for teams wanting a self-hosted, tool-agnostic CI/CD system, though Azure DevOps has tighter native integration if your entire stack is already in Azure.
Can I run multiple .NET SDK versions side by side on one Jenkins agent? Yes — the .NET SDK installs side by side by design, so an agent can have SDK 6, 7, and 8 installed simultaneously. Use a global.json file in your repository to pin the exact SDK version a given project should build with, which keeps builds reproducible even as newer SDKs get installed on the same agent later.
How do I handle solution files with multiple projects targeting different frameworks? dotnet build/dotnet restore at the solution level (.sln) handles mixed-framework projects automatically, resolving each project’s dependencies independently, so you generally don’t need separate pipeline stages per project unless you want independent test reporting or parallelization.
Summary
Setting up Jenkins for .NET projects is mostly about getting the right SDK on the right agent type — Linux for modern cross-platform .NET, Windows for legacy .NET Framework — and then wiring together dotnet restore, build, test, and publish into a clean pipeline. From there, Docker packaging, SonarQube analysis, and deployment to Azure or any other target slot in as additional stages. Once the pipeline is in place, .NET teams get the same fast feedback loop that Java or Node teams have enjoyed for years.
