Publishing A Single EXE File In .NET Core 3.0

Say I have a simple “Hello World” console application that I want to send to a friend to run. The friend doesn’t have .NET Core installed so I know I need to build a self contained application for him. Easy, I just run the following command in my project directory :

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

Pretty self explanatory :

  • Publish the project from the current directory
  • Build the project to run on Windows 64 bit machines
  • Build in release configuration mode
  • Publish everything as “self-contained” so that everything required to run the app is packaged up with our executable

So this works right, we end up with a folder that has our exe and everything that is required to run it, but the issue is that there is a tonne required to run even a HelloWorld console app.

All up it’s a little over 200 files. Crazy – but it makes sense. It’s essentially having to package the .NET Core runtime just to run Hello World.

So functionally, this works. But optics wise, it looks like a mess. I’ve sent folders like this to clients and had to say “uhh.. So I’m going to send you a folder with hundreds of files in it… But… Can you just find the one titled MyApplication.exe and run that and don’t worry about the rest?”. When people are used to having an icon on their desktop they double click and things just.. work… This just doesn’t cut it.

You Need .NET Core 3.0 (Preview 5+)

I need to put that in bold because this will not work if you don’t have .NET Core 3.0. And if you are an early adopter (At the time of this post .NET Core 3.0 is not GA yet), you will need atleast preview 5 upwards. If you’re not one to mess around in preview’s, then you can just wait a few months till .NET Core 3.0 is released!

The PublishSingleFile Flag

All that intro and it literally comes down to a single command flag :

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

All this does is runs our publish command but tells it to package it within a single file. You’ll notice that we no longer specify the self-contained flag. That’s because it’s assumed that if you are packaging as a single exe, that you will want all it’s dependencies along with it. Makes sense.

And the output :

A single tidy exe! When this is executed, the dependencies are extracted to a temporary directory and then everything is ran from there. It’s essentially a zip of our previous publish folder! I’ve had a few plays around with it and honestly, it just works. There is nothing more to say about it. It just works.

Helpful Tip : Make sure you clean your publish directory or just outright delete your bin folder. It doesn’t break anything to not do so, but all those old DLL’s just hang around until you do so your nice single EXE is hard to spot. 

File Size And Startup Cost

Keen eyes will notice something about the above screenshot. The file size. It’s over 70MB! That’s crazy for an application that does nothing but print Hello World to the screen! This is solved in Preview 6 of .NET Core 3.0 with a feature called IL Linker or Publish trimmer that omits DLL’s that aren’t used. You can read more about that here!

The other issue you may find is that there is a slight startup cost when running the self contained executable for the first time. Because it needs to essentially unzip all dependencies to a temporary directory on first run, that’s going to take a little bit of time to complete. It’s not crazy (5 seconds or so), but it’s noticeable. Luckily on subsequent runs it uses this already unzipped temp folder and so startup is immediate.

Join over 3.000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.


  1. Wade,
    Do you know where does the temp directory get created? We have been running into problems of missing dependencies and unnecessarily modified or removed files from the installation folder because customers didn’t know. This approach will help us considerably. However if the temp directory location is locked down, which it is via group policies on Enterprise networks, then this would become another battle. Similar to ClickOnce application. Anyway, I was just curious about it after reading your post. Very informative post.

    1. Honestly. No. And I did have a quick search for it both on my PC and in all documentation. It says “A” Temp directory but not “THE” Temp directory which made me think it will be something in your user profile or similar. I’ll keep looking and let you know.

      1. Fyi, I found the temp directory is under my user folder at below:
        C:\Users\[my login]\AppData\Local\Temp\.net

    2. The easiest way to figure out these kind of details is by using procmon from sysinternals (basically dtrace for Windows)

      Run it as admin, then start the program and check the file open kernel calls.

    3. If you put this line of code in your Hello World program:


      And publish that into a single executable. It will tell you where it runs with all the dlls that the program is referencing to

  2. Thanks for writing this up. Super helpful. I struggled for hours with dotnet-warp and fody, presumably due to compatibility issues because I’m on the preview dotnetcore sdk. This just worked, first shot. Yeah, the binary is enormous, but that too will be solved. This bodes well for the future of .net core on the desktop.

  3. Hi Wade

    Thanks for writing the article. This is very very useful. However, when I try to run the exe I get the following error: “The application to execute does not exist: myapp.dll”. Did you encountered this as well? And if so, how you solved it?

  4. Download fody
    From nuget and
    Just click to build

    Best and most simple way to create single exe file from visual studio

  5. I just tried this and can’t get my publish command to do both SingleFile as well as ReadyToRun.

    The only way I can get’em both, is if I include these settings in the project file.

    I can easily see whether the files got SingleFiled or not, but for determining whether they were also ReadyOrRunned, I simply look at the resulting .exe file size.

    When publishing SingleFile via the command line, my .exe stays the same size as when I only SingleFile, but don’t ReadyToRun, in my project file.

    When I SingleFile and ReadyToRun in my project file, the .exe becomes a lot bigger. I need to get it the same size from the command line, so I’ll know it worked.

    I tried explicitly specifying the –self-contained param after the -p:PublishSingleFile=true param. It still doesn’t work.

    Also, the SingleFile/ReadyToRun compilation was generating .exe files a few MB larger than it is now, even though I didn’t change any code since then. That’s pretty strange, since compilation is supposed to be a fairly deterministic operation, aside from the occasional datetime stamp here and there.

    Anybody else experience this?

  6. More interestingly it would be useful to determine where the location of the bundle exe resides. I haven’t yet found a way to do this and it doesn’t appear to be documented. If you could do this you can pack your appsettings.config external to the bundle exe, and load it from there instead of the temp folder location. No one wants to explore for a temp folder to edit appsettings.config, that’s just terrible for end user configuration.

    1. It would make it “harder” but it wouldn’t protect you. Your code is still there, just inside a different bundle. Really only code obfuscation can help you if you really don’t want someone to decompile (Or maybe your DRM as complex as possible).

  7. Wow. I guess I’ll just stick with the old .Net Framework whenever the target audience is Windows.

Leave a Reply

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