Skip to main content

.NET Deployment Models

When you've built your .NET application and are ready to distribute it to users, you need to choose an appropriate deployment model. The deployment model affects how your application is packaged, distributed, and executed on target systems. This article explores the different .NET deployment models, helping you understand which one best suits your specific needs.

Introduction to .NET Deployment Models

.NET offers several deployment models that provide flexibility in how you distribute your applications. Each model has specific advantages and trade-offs related to factors such as:

  • Size of deployment package
  • Dependencies management
  • Cross-platform compatibility
  • Execution environment requirements
  • Security considerations
  • Update management

Understanding these models is crucial for delivering your applications efficiently and ensuring they run smoothly in their target environments.

Framework-Dependent Deployment (FDD)

What is Framework-Dependent Deployment?

Framework-dependent deployment is the default deployment model for .NET applications. In this model, your application package contains only your application code and third-party dependencies, but not the .NET runtime and framework libraries.

Key Characteristics

  • Smaller deployment package: Since the .NET runtime isn't included
  • Requires .NET runtime: Must be installed on the target system
  • Version compatibility: May benefit from runtime updates and patches

How to Create a Framework-Dependent Deployment

To publish a framework-dependent application, use the following command:

bash
dotnet publish -c Release

Or specify it explicitly:

bash
dotnet publish -c Release --self-contained false

Example Project

Let's look at a simple console application and publish it using the framework-dependent model:

  1. Create a new console application:
bash
dotnet new console -n HelloFDD
cd HelloFDD
  1. Add some code in Program.cs:
csharp
Console.WriteLine("Hello from a Framework-Dependent Deployment!");
Console.WriteLine($"Running on .NET version: {Environment.Version}");
Console.WriteLine($"OS Platform: {Environment.OSVersion}");
  1. Publish the application:
bash
dotnet publish -c Release
  1. The output directory (usually bin/Release/net7.0/publish/ depending on your .NET version) will contain your application files without the .NET runtime.

Real-World Use Case

Framework-dependent deployment is ideal for:

  • Internal enterprise applications where the .NET runtime is standardized
  • Applications targeting environments where .NET is already installed
  • Reducing download size for web applications or frequent updates

Self-Contained Deployment (SCD)

What is Self-Contained Deployment?

Self-contained deployment packages your application along with the .NET runtime and all necessary libraries. This allows your application to run without requiring .NET to be installed on the target system.

Key Characteristics

  • No external dependencies: Includes everything needed to run the application
  • Larger deployment size: Contains the complete runtime
  • Platform-specific: Must publish for each target platform separately
  • Version isolation: Uses the packaged runtime regardless of what's installed on the system

How to Create a Self-Contained Deployment

To create a self-contained deployment, you need to specify both the --self-contained flag and the target runtime identifier (RID):

bash
dotnet publish -c Release --self-contained true -r win-x64

Common runtime identifiers include:

  • win-x64 (Windows 64-bit)
  • linux-x64 (Linux 64-bit)
  • osx-x64 (macOS 64-bit)

Example Project

Let's publish our application as self-contained:

  1. Using the same project from the previous example, publish it as self-contained:
bash
dotnet publish -c Release --self-contained true -r win-x64
  1. The output directory will now include the .NET runtime components along with your application.

Real-World Use Case

Self-contained deployments are ideal for:

  • Desktop applications distributed to users with varying environments
  • Applications where you need to ensure a specific .NET version
  • Scenarios where you can't assume .NET is installed on the target system

Single-File Deployment

What is Single-File Deployment?

Single-file deployment packages your application as a single executable file that contains all dependencies, making distribution simpler.

Key Characteristics

  • Simplified distribution: One file contains everything
  • Can be self-contained or framework-dependent
  • Extraction behavior: Files are extracted to a temporary directory at runtime

How to Create a Single-File Deployment

To create a single-file deployment, use the PublishSingleFile property:

bash
dotnet publish -c Release --self-contained true -r win-x64 /p:PublishSingleFile=true

Configuring Single-File in Project File

You can also configure this in your project file:

xml
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>

Real-World Use Case

Single-file deployments are perfect for:

  • Command-line tools where simple installation is important
  • Utility applications that need to be easily copied and executed
  • Applications where you want to minimize the complexity for end users

Ready-to-Run (R2R) Compilation

What is Ready-to-Run Compilation?

Ready-to-Run is a form of ahead-of-time (AOT) compilation that improves startup performance by pre-compiling assemblies to native code.

Key Characteristics

  • Faster startup time: Reduces JIT compilation at runtime
  • Larger deployment size: Contains both IL and pre-compiled code
  • Platform-specific: Must target specific runtime identifiers

How to Enable R2R Compilation

To enable Ready-to-Run compilation, use the PublishReadyToRun property:

bash
dotnet publish -c Release --self-contained true -r win-x64 /p:PublishReadyToRun=true

Example in Project File

xml
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>

Real-World Use Case

Ready-to-Run compilation is beneficial for:

  • Applications where startup performance is critical
  • Desktop applications that need to feel responsive immediately
  • Services that need to quickly start and handle requests

Trimmed Deployment

What is Trimming?

Trimming is the process of removing unused code from your application and its dependencies to reduce the deployment size.

Key Characteristics

  • Reduced size: Removes unused code paths
  • Potential compatibility issues: May remove code accessed via reflection
  • Works best with self-contained apps: Most effective when combined with self-contained deployment

How to Enable Trimming

To enable trimming, use the PublishTrimmed property:

bash
dotnet publish -c Release --self-contained true -r win-x64 /p:PublishTrimmed=true

Configuration in Project File

xml
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>

Handling Trimming Warnings

When using trimming, you may need to configure trim warnings or provide hints about reflection usage:

xml
<ItemGroup>
<TrimmerRootAssembly Include="MyAssembly" />
</ItemGroup>

Real-World Use Case

Trimming is ideal for:

  • IoT applications with limited storage
  • Mobile applications where size matters
  • Applications deployed to environments with bandwidth constraints

Container-Based Deployment

What is Container-Based Deployment?

Container-based deployment packages your application along with its dependencies into a container that can be run consistently across different environments.

Key Characteristics

  • Consistent execution environment: Runs the same way everywhere
  • Isolation: Application runs in its own container with defined resources
  • Easy scaling: Facilitates horizontal scaling in container orchestration systems

Creating a Docker Container for .NET

  1. Create a Dockerfile:
dockerfile
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["MyApp.csproj", "./"]
RUN dotnet restore "MyApp.csproj"
COPY . .
RUN dotnet build "MyApp.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]
  1. Build the Docker image:
bash
docker build -t myapp:latest .
  1. Run the container:
bash
docker run -p 5000:80 myapp:latest

Real-World Use Case

Container-based deployment is ideal for:

  • Microservices architecture
  • Cloud-native applications
  • Applications requiring consistent execution environments across development, testing, and production

Cloud-Native Deployment

What are Cloud-Native Deployments?

Cloud-native deployments are specifically optimized for cloud environments, often leveraging platform-as-a-service (PaaS) offerings.

Common Cloud Deployment Options

  1. Azure App Service:

    bash
    dotnet publish -c Release
    az webapp up --name MyAzureWebApp --resource-group MyResourceGroup --plan MyAppServicePlan
  2. AWS Elastic Beanstalk:

    bash
    dotnet publish -c Release
    # Package your published output and deploy via AWS CLI or console
  3. Using GitHub Actions for CI/CD:

    yaml
    name: Deploy to Azure
    on:
    push:
    branches: [ main ]
    jobs:
    build-and-deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Setup .NET
    uses: actions/setup-dotnet@v3
    with:
    dotnet-version: 7.0.x
    - name: Publish
    run: dotnet publish -c Release -o publishOutput
    - name: Deploy to Azure
    uses: azure/webapps-deploy@v2
    with:
    app-name: 'mywebapp'
    publish-profile: ${{ secrets.AZURE_PUBLISH_PROFILE }}
    package: ./publishOutput

Real-World Use Case

Cloud-native deployments are suitable for:

  • Applications requiring high availability and scalability
  • Services that need to scale based on demand
  • Applications benefiting from cloud provider integrations

Comparison of Deployment Models

Deployment ModelPackage SizeDependenciesCross-PlatformStartup TimePrimary Use Case
Framework-dependentSmallRequires .NET runtimeYes (with runtime)ModerateInternal apps, shared environments
Self-containedLargeNoneNo (platform-specific)ModerateEnd-user applications
Single-fileMedium/LargeVariesNo (platform-specific)ModerateCommand-line tools, utilities
Ready-to-RunLargeNoneNo (platform-specific)FastPerformance-critical apps
TrimmedMediumNoneNo (platform-specific)ModerateSize-constrained environments
ContainerLargeNone (container runtime)Yes (with container runtime)ModerateMicroservices, cloud applications

Choosing the Right Deployment Model

When selecting a deployment model, consider:

  1. Target environment: Do users have .NET installed? Do you have control over the environment?
  2. Size constraints: Is download size or disk usage a concern?
  3. Performance requirements: Is startup time critical?
  4. Update strategy: How will you deploy updates?
  5. Cross-platform needs: Does your app need to run on multiple platforms?

Summary

.NET offers a rich set of deployment models to suit various application needs:

  • Framework-dependent deployment is smaller and simpler but requires .NET to be installed
  • Self-contained deployment packages everything needed to run but results in larger deployment size
  • Single-file deployment simplifies distribution with one executable file
  • Ready-to-Run compilation improves startup performance at the cost of size
  • Trimming reduces size by removing unused code
  • Container-based deployment ensures consistent execution across environments
  • Cloud-native deployment optimizes for cloud platforms and services

By understanding these models and their trade-offs, you can choose the most appropriate approach for your application, ensuring efficient distribution and optimal execution in your target environment.

Additional Resources

Exercises

  1. Create a simple console application and publish it using three different deployment models. Compare the size and performance of each.

  2. Set up a CI/CD pipeline using GitHub Actions to deploy a .NET application to Azure App Service.

  3. Create a Docker container for a .NET Web API and deploy it to a container registry.

  4. Build a self-contained application with trimming enabled and analyze which assemblies were trimmed and the resulting size reduction.

  5. Create a single-file application with Ready-to-Run compilation and measure its startup performance compared to a standard framework-dependent deployment.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)