Using Dotnet-Warp with Azure DevOps CI/CD Pipeline
Published: 2020-10-23
Introduction
Starting with .NET Core 3.0, we have the option to publish applications as self-contained deployments. A self-contained deployment includes the application and its dependencies, including the .NET runtime. This allows us to deploy applications without requiring the target machine to have the .NET runtime installed separately.
However, this deployment model comes with a couple of challenges.
1. Too many files
A typical self-contained deployment produces a publish folder containing hundreds of files. The number of files can increase as we add more dependencies to our application.
This can make deployment and distribution inconvenient. Users have to locate the correct executable among all the published files. We can work around this by providing shortcuts or PowerShell scripts that launch the appropriate executable, but these are additional things to maintain.
2. Deployment size
Not every application needs every runtime component included in the deployment. Removing unused components can significantly reduce the overall deployment size.
.NET provides several publishing options to address these challenges. Combining a self-contained deployment with the Produce single file and Trim unused assemblies options can produce a much smaller, single executable.
For more details about .NET deployment models and file publishing options, check out my earlier article: Understanding .NET Core Deployment Models.
What is dotnet-warp?
Warp is a tool written in Rust that can bundle an application and its dependencies into a single executable.
Hubert Rybak created a .NET wrapper around Warp called dotnet-warp.
I tested dotnet-warp with a few of my internal projects and found that it produced smaller executables than the built-in .NET single-file publishing approach.
The following image compares the executable sizes produced by the built-in publishing mechanism and dotnet-warp.

In this particular example, dotnet-warp produced an executable approximately 5 MB smaller than the built-in approach. While the difference might not seem significant for a small application, I noticed that the size difference tended to increase as the number of dependencies grew.
Let’s see how to install and use dotnet-warp.
Installing dotnet-warp
dotnet-warp is distributed as a .NET tool. This means we can install and use it just like other .NET CLI tools.
To install version 1.1.0 globally, run the following command:
dotnet tool install --global dotnet-warp --version 1.1.0
This installs dotnet-warp as a global tool.
What does global installation mean?
Global installation does not mean that the tool is installed at the machine level. Instead, it is installed for the current user and can be executed by that user without specifying the full executable path.
If you want to install the tool locally for a project, omit the --global option and use a local tool manifest.
Using dotnet-warp
To demonstrate the available arguments, let’s use a sample .NET Core console application.
Sample application
Here are the details of the sample application used in this article:
- Project name:
Dhrutara.DotnetWarp - IDE: Visual Studio 2019
- Target framework: .NET Core 3.1
- Additional dependency: Newtonsoft.Json (added as a dependency but not used in the application)
The project file is named Dhrutara.DotnetWarp.csproj.
Required argument
The only required argument for dotnet-warp is the project file path. You can provide either a relative or an absolute path.
Let’s start with the simplest possible command.
- Open PowerShell or Command Prompt.
- Navigate to the project folder.
- Execute the following command:
dotnet-warp ".\Dhrutara.DotnetWarp.csproj"

Notice that the executable is created in the current working directory, where the command is executed.
Navigating to the project folder is optional. If you execute the command from another directory, simply provide the full path to the project file.
Specifying the output location
By default, dotnet-warp creates the executable in the current working directory.
If you want to place the executable in a different location, use the --output option.
For example:
dotnet-warp ".\Dhrutara.DotnetWarp.csproj" --output ".\publish\Dhrutara.DotnetWarp.exe"

Note that the output argument should include the complete executable filename, including the .exe extension on Windows.
Passing additional .NET build arguments
Internally, dotnet-warp uses the .NET build tooling. It also allows us to pass additional MSBuild properties to customize the build.
The syntax for passing a property is:
-p:<property-name>=<property-value>
By default, dotnet-warp builds the application in Release configuration.
Let’s assume we want to build our Dhrutara.DotnetWarp application in Debug configuration and set its version to 1.2.1.
We can achieve this by passing the appropriate MSBuild properties:
dotnet-warp ".\Dhrutara.DotnetWarp.csproj" --output ".\publish\Dhrutara.DotnetWarp.exe" -p:configuration="debug" -p:Version="1.2.1"
As you can see, we can customize the build configuration and application version without changing the project file.
Integrating dotnet-warp with an Azure DevOps CI/CD Pipeline
Now that we understand how to use dotnet-warp from the command line, integrating it into an Azure DevOps pipeline is straightforward.
We simply need to add a PowerShell task to the deployment stage and execute the same commands.
Here is the YAML configuration I used:
- task: PowerShell@2
displayName: 'Warp it'
inputs:
targetType: 'inline'
script: |
New-Item -Path "$(deployFolder)" -ItemType Directory
dotnet tool update dotnet-warp --global
dotnet-warp "$(Build.SourcesDirectory)\Dhrutara.DotnetWarp\Dhrutara.DotnetWarp.csproj" --output "$(deployFolder)\Dhrutara.DotnetWarp.exe" --verbose -p:configuration="${{parameters.buildConfiguration}}" -p:runtime="${{parameters.runtimeIdentifier}}"
There are three important commands in the PowerShell script.
Step 1: Create the deployment folder
New-Item -Path "$(deployFolder)" -ItemType Directory
dotnet-warp does not automatically create the output directory if it doesn’t exist. Therefore, we create the deployment folder before running the tool.
Step 2: Install or update dotnet-warp
dotnet tool update dotnet-warp --global
We use dotnet tool update instead of dotnet tool install because the update command handles both installation and updates:
- If the tool isn’t installed, it installs it.
- If an older version is installed, it updates the tool.
- If the latest available version is already installed, it does nothing.
This makes the pipeline easier to maintain because we don’t need separate installation and update steps.
Step 3: Run dotnet-warp
dotnet-warp "$(Build.SourcesDirectory)\Dhrutara.DotnetWarp\Dhrutara.DotnetWarp.csproj" --output "$(deployFolder)\Dhrutara.DotnetWarp.exe" --verbose -p:configuration="${{parameters.buildConfiguration}}" -p:runtime="${{parameters.runtimeIdentifier}}"
Finally, we execute dotnet-warp with the project file and output location.
A few things to note:
$(Build.SourcesDirectory)points to the directory where Azure DevOps checks out the source code.$(deployFolder)is a pipeline variable containing the deployment output directory.${{parameters.buildConfiguration}}specifies the build configuration, such as Debug or Release.${{parameters.runtimeIdentifier}}specifies the target runtime identifier, such aswin-x64.--verboseenables additional diagnostic output.
The build configuration and runtime identifier are passed as MSBuild properties, allowing the pipeline to control the build without modifying the project file.
A few additional notes
If you have used .NET CLI tools before, you might have noticed that I’m invoking dotnet-warp directly rather than using the usual dotnet <tool-name> syntax.
Normally, a .NET tool can be invoked using:
dotnet dotnet-warp
However, I encountered an Azure Pipelines error:
Sequence contains more than one matching element
when invoking the tool with the dotnet prefix.
After some trial and error, I found that invoking dotnet-warp directly worked in my pipeline.
If you encounter issues with this approach in a different environment, you can try invoking it through the .NET CLI instead.
For the complete sample application and Azure DevOps pipeline configuration, check out the GitHub repository.
Conclusion
dotnet-warp is a useful tool to consider when you want to further reduce the size of self-contained .NET application deployments.
It can be particularly helpful when distributing applications as single executables and when deployment size matters.
As demonstrated, integrating it into an Azure DevOps CI/CD pipeline requires only a few additional commands. Once configured, the pipeline can automatically produce the executable as part of your deployment process.