← Back to Tutorials Chapter 11

Build & Deployment

This chapter explores the build system in depth, CI/CD pipeline integration, Docker containerization, and the various deployment models available for .NET Core applications.

MSBuild Deep Dive

MSBuild is the build engine behind .NET Core. Every .csproj file is an MSBuild project file. You can extend the build pipeline with custom targets.

Custom Targets

<Target Name="PostBuild" AfterTargets="Build">
  <Copy SourceFiles="$(OutputPath)$(AssemblyName).dll"
        DestinationFolder="$(SolutionDir)dist/"/>
</Target>

BeforeBuild / AfterBuild

Override the built-in BeforeBuild and AfterBuild targets to inject pre- or post-processing logic:

<Target Name="BeforeBuild">
  <Message Importance="high" Text="Building $(ProjectName)..." />
</Target>

MSBuild Properties and Items

Common properties: OutputType, TargetFramework, AssemblyName, RootNamespace. Items like Compile, Content, EmbeddedResource define what gets built.

CI/CD Pipelines

Modern teams automate builds and deployments using continuous integration and delivery pipelines.

GitHub Actions

name: .NET Core Build
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: 8.0.x
    - name: Restore
      run: dotnet restore
    - name: Build
      run: dotnet build --no-restore -c Release
    - name: Test
      run: dotnet test --no-build -c Release
    - name: Publish
      run: dotnet publish -c Release -o artifacts

Azure DevOps Pipelines

trigger:
- main
pool: ubuntu-latest
steps:
- task: DotNetCoreCLI@2
  inputs:
    command: 'test'
    arguments: '--configuration Release'

Docker Containers

Containerizing .NET apps is straightforward with multi-stage Dockerfiles:

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app .
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApp.dll"]
Use the aspnet image for ASP.NET Core apps and the smaller runtime image for console apps. Consider Alpine-based images for smaller footprints.
.NET Core build and deployment

Deployment Models

Framework-dependent deployment (FDD) requires the .NET runtime on the target machine. It produces smaller deployments.

Self-contained deployment (SCD) bundles the runtime and framework libraries together. Larger but entirely independent.

dotnet publish --self-contained true --runtime win-x64
dotnet publish --self-contained false       # Default

Single-File Publishing

Packages the entire application into a single executable:

dotnet publish -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true

ReadyToRun (R2R)

R2R compiles IL to native code at publish time, reducing JIT overhead at startup:

dotnet publish -c Release -r win-x64 --self-contained true -p:PublishReadyToRun=true
R2R images are larger but start significantly faster — ideal for containerized and serverless workloads.

Trimming

Publish trimming removes unused assemblies to reduce deployment size:

dotnet publish -p:PublishTrimmed=true -p:TrimMode=Link
Exercise: Take any .NET Core web API project. Create a GitHub Actions workflow that builds, runs tests, publishes a self-contained single-file executable for linux-x64, and builds a Docker image. Verify the workflow passes on push.