Self-contained executable with .NET Core 3.0 on Windows, Linux and Raspberry

.NET Core 3.0 comes with support for self contained-executables. It means we can publish applications as a single executable for specified platform. Also trimming – removing of unused code from assemblies – is supported. This blog post demonstrates how to build self-contained executable using .NET Core 3.0.

NB! As of writing this post the current version of .NET Core 3.0 is Preview 7. Not everything is stable yet and over coming versions we will probably see many improvements to self-contained executables.

What is self-contained executable?

Before any coding I will introduce some important terms.

  • Self-contained executable – executable file containing program and all needed assemblies (one exe-file we can run).
     
    Making of self-contained-executable
     
  • Trimmed executable – self-contained executable where dependent assemblies have all not used code removed (if StringBuilder is not used then StringBuilder is removed from executable). Trimmed executables can be much smaller than original ones due to removed code.
     
    Trimming of self-contained executable

Trimmed executable is risky business. Programs may use reflection. Types loaded through reflection may easily fly under the radar and be victioms of trimming. It is possible to specify what types cannot be trimmed. I don’t stop on this topic right now. But trimmed self-contained executable is desired maximum in this blog post.

Getting started

Let’s start with golden classics of our industry – the almighty “Hello, world” written in C#.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
    }
}

We can publish it like we have done this far by typing:

dotnet public -c Release -r win10-x64

What we get is publish folder with load of files needed by our application:

  • 255 files
  • 65,8 MB

In Windows Explorer we can see a lot of files in publishing folder.

Assemblies of .NET Core application

Most of these files are actually very small and why not link all assemblies together to one file.

Publishing self-contained executable

Let’s publish application now as self-contained executable by typing:

dotnet publish -c Release -r win10-x64 /p:PublishSingleFile=true

Things just got better:

  • 2 files
  • 65,8 MB

We have now one executable and its program debug database (pdb-file).

Trimming self-contained executable

We are not done yet. We have platform specific assemblies now in one file with our application but there’s tons of code our application doesn’t use.

Let’s trim out unused code. To get trimmed self-contained executable we have to type:

dotnet publish -c Release -r win10-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true

Now things are nice in our publish folder:

  • 2 files
  • 25,3 MB

But we have a warning in publishing output:

Optimizing assemblies for size, which may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink

This time we were lucky – our program is so primitive that there’s no chance for something to go wrong.

Self-contained web application

Let’s try what happens with default ASP.NET Core MVC application when published as self-contained executable. Just create default web application and publish it.

dotnet public -c Release -r win10-x64

This is what we will get:

  • 87,3 MB
  • 393 files, 13 folders

Let’s make self-contained executable now by typing:

dotnet publish -c Release -r win10-x64 /p:PublishSingleFile=true

And the result is weird:

  • 87,4 MB
  • 3 files, 0 folders

Where is wwwroot? Let’s run application to see what happened.

Self-contained web application with no styles

Content root is missing. Let’s copy wwwroot folder to publish folder and try again (also settings file were not published but we don’t need these for this example anyway).

Self-contained web application is ready for deployment

The result is same although we have content folder available. We have to stop web application and run it again to make it understand that wwwroot folder is there.

Self-contained web application with styles

One thing to notice – web application starts extremely fast.

Trimmed self-contained web application

Let’s try what happens to web application if we publish it as trimmed self-contained executable. The publish command is same as above:

dotnet publish -c Release -r win10-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true

What we got is:

  • 52,5 MB
  • 3 files, 0 folders

Okay, we need to copy wwwroot to publish folder again.

Web application is different beast and it comes with many assemblies. It’s interesting to find out if trimmed executable works or not. It comes up fast and there are no errors.

Let’s make first request as our application is currently just started. And the troubles start… This is how our first request ends.

An unhandled exception has occurred while executing the request.
System.IO.FileNotFoundException: Could not load file or assembly 'System.Threading.Tasks, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
File name: 'System.Threading.Tasks, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

From ILLink.Tasks documentation (referred through short link in console window after publishing trimmed executable) we can read:

“Applications or frameworks (including ASP.NET Core and WPF) that use reflection or related dynamic features will often break when trimmed, because the linker does not know about this dynamic behavior, and can not determine in general which framework types will be required for reflection at runtime. To trim such apps, you will need to tell the linker about any types needed by reflection in your code, and in packages or frameworks that you depend on. Be sure to test your apps after trimming.”

In this point I ran out of ideas as adding reference to System.Threading.Tasks didn’t work and also suggestion to use something like typeof(Task) somewhere in code didn’t worked out. I think trimming of web application is more complex topic and it’s worth to come back to it when .NET Core 3.0 is released.

Running self-contained web application on Windows 10 IoT Core

I hoped to get trimmed executable work because it’s only good when web application running on board is as minimal as possible. But self-contained executable is also okay.

Before publishing we need to change Program.cs file of web application and make web application to listen all interfaces in port 5000.

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
            webBuilder.UseUrls("http://*:5000/");
        });

Make sure this port is open on board (more information is available in my blog post Installing ASP.NET Core 3.0 on RaspberryPi and Windows 10 IoT Core).

Let’s publish our web application now for Windows 10 Iot Core:

dotnet publish -c Release -r win10-arm /p:PublishSingleFile=true

After publishing we have to copy wwwroot from project folder to publish folder. Easiest way to copy web application to Windows 10 IoT Core is using Windows Explorer.

After this we have to log in to Windows 10 IoT Core using PowerShell and run web application from there.

Self-contained web application with styles

On my old RaspberryPi 2 the web application is not very demanding:

  • CPU: up to 5%
  • Working set: 43.2MB

And best of all things – it works smooth.

Using project file

We don’t have to use command-line arguments to link and trim executables. It’s possible to add these settings to project file instead.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <!-- comment out for web application -->
    <PublishTrimmed>true</PublishTrimmed>
    <PublishReadyToRun>true</PublishReadyToRun>
    <PublishSingleFile>true</PublishSingleFile>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
  </PropertyGroup>

</Project>

You can find out more about it from excellent blog post Making a tiny .NET Core 3.0 entirely self-contained single executable by Scott Hanselman.

Running self-contained executables on Linux

Self-contained executables can be run also on Linux. As Jeremy Morgan points out in his blog post Creating Trimmed Self Contained Executables in .NET Core then self-contained executables published for Linux doesn’t require installation of .NET Core on Linux but Prerequisites for .NET Core on Linux are required.

NB! Publishing ready-to-run executables on Windows 10 for Linux is currently not supported but you can use Windows Subsystem for Linux (WSL). To get WSL running on Windows 10 head over to my blog post Running ASP.NET Core applications on Windows Subsystem for Linux but skip the part of installing .NET Core.

First install .NET Core 3.0 on Linux that you are running with WSL.

You can access your computer disks by going to /mnt folder and moving further to disk you like. I built “Hello, World” console application with the following command.

sudo dotnet publish -r linux-x64 -c Release

It is published to same location as in Windows and best of all – it worked too.

Wrapping up

Self-contained and trimmed executables are interesting feature of coming .NET Core 3.0. For some programs it makes sense to have just one executable containing everything needed to run it. I see trimming as most important feature. Minimal trimmed executables are perfect fit for applications running on IoT boards. I hope that stable release of .NET Core 3.0 supports also ASP.NET Core with trimming.

Liked this post? Empower your friends by sharing it!

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    One thought on “Self-contained executable with .NET Core 3.0 on Windows, Linux and Raspberry

    Leave a Reply

    Your email address will not be published. Required fields are marked *