Scott Hanselman

Self-contained .NET Core Applications

September 18, 2016 Comment on this post [29] Posted in DotNetCore | Open Source
Sponsored By

Just in case you missed it, .NET is all open source now and .NET Core is a free, open source, cross-platform framework that you can download and start with in <10 minutes. You can get it on Mac, Windows, and a half-dozen Unixes at Take that along with the free, cross-platform Visual Studio Code and you'll be writing C# and F# all over the place.

Ok, that said, there's two ways to deploy a .NET Core application. There's FDD and SCD. Since TLAs (three letter acronyms) are stupid, that's Framework-dependent and Self-contained. When .NET Core is installed it ends up in C:\program files\dotnet on Windows, for example. In the "Shared" folder there's a bunch of .NET stuff that is, well, shared. There may be multiple folders, as you can see in my folder below. You can have many and multiple installs of .NET Core.

When you're installing your app and its dependencies BUT NOT .NET Core itself, you're dependent on .NET Core already being on the target machine. That's fine for Web Apps or systems with lots of apps, but what if I want to write an app and give it to you as zip or on a USB key and and I just want it to work. I'll include .NET Core as well so the whole thing is a Self Contained Deployment.

It will make my "Hello World" application larger than if I was using an existing system-wide install, but I know it'll Just Work because it'll be totally self-contained.

Where is .NET Core installed to?

If I deploy my app along with .NET Core it's important to remember that I'll be responsible for servicing .NET Core and keeping it up to date. I also need to decide on my target platforms ahead of time. If I want it to run on Windows, Mac, and Linux AND just work, I'll need to include those target platforms and build deployment packages for them. This all makes mostly intuitive sense but it's good to know.

I'll take my little app (I'm just using a "dotnet new" app) and I'll modify project.json in a text editor.

My app is a .NETCore.App, but it's not going to use the .NET Core platform that's installed. It'll use a local version so I'll remove "type="platform"" from this dependency.

"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.1"

Next I'll make a runtimes section to specify which ones I want to target. There's a list of ALL the Runtime IDs here.

"runtimes": {
"win10-x64": {},
"osx.10.10-x64": {},
"ubuntu.14.04-x64": {}

After running "dotnet restore" you'll want to build for each of these like this:

dotnet build -r win10-x64
dotnet build -r osx.10.10-x64
dotnet build -r ubuntu.14.04-x64

And then publish release versions after you've tested, etc.

dotnet publish -c release -r win10-x64
dotnet publish -c release -r osx.10.10-x64
dotnet publish -c release -r ubuntu.14.04-x64

Once this is done, I've got my app self-contained in n folders, ready to deploy to whatever systems I want.

Self-contained .NET app built for 3 OSs

You can see in the Win10 folder there's my "MYAPPLICATION.exe" (mine is called scd.exe) that can be run, rather than running things like developers do with "dotnet run."

I run foo.exe, not dotnet.exe now

There's lots of good documentation about how you can tune and define exactly what gets deployed with your self contained application over at the .NET Core Docs. You can do considerable trimming to .NET Core, and there's talk of that becoming more and more automated in the future, possibly down to the method level.

Sponsor: Big thanks to Redgate for sponsoring the feed this week. Discover the world’s most trusted SQL Server comparison tool. Enjoy a free trial of SQL Compare, the industry standard for comparing and deploying SQL Server schemas.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
September 18, 2016 9:38
You might want to mention that the .exe isn't your app as a .exe but just a wrapper around dotnet.exe (or .dmg, or whatever linux uses) as a shortcut to running it yourself. It's not like compiling a full .net executible even though it looks like that.
September 18, 2016 10:07
Shawn - Ya, I went around with how DEEP to go...I guess I'll do a follow up with the actual deep deep details.
September 18, 2016 10:39
Hey, that is a wonderfull article. Thx alot.

Now I got an documentation about the framework IDs too thx alot:)

Btw: Your link to the runtime IDs is broken, there you have an extra ' character at the end :).
September 18, 2016 11:40
hey Shawn, I did not get when you said .exe isn't your app as a .exe. I understood scd.exe as compiled version of my application only. but got confused with ur comment.
September 18, 2016 12:16
Runtime IDs link is 404 for me on iOS for some reason.
September 18, 2016 12:44
@Rui - the Runtime IDs link is 404 because there's an extra single quote at the end. Here's the working link:
September 18, 2016 14:43
Nice, I'd love to see Winforms or WPF apps supported with by this.
September 18, 2016 17:53
So if I run the publish commands, what would someone on ubuntu or mac need to do to run the application if I gave them those files on a thumbdrive?
September 18, 2016 21:19
@Tony, Winforms and WPF will require a much fuller version of .Net than is present in Core.

However, I wouldn't mind a stripped down version of Winforms, windowing with basic controls could take you a long way, IMO.
September 18, 2016 21:22
This was a very useful tutorial. You're right that there's a lot to read through in terms of documentation but it's a bit fragmented and I couldn't see anything on how to do what's been done here.

I spent a couple of nights recently playing around with the binary installations trying to get a deeper understanding of how it all hangs together and I'll be honest I'm still a bit lost.

Previously with dnvm I could run dnvm list this would give me all the runtimes installed and I could easily switch with dnvm use. But now it isn't clear (to me at least) how you switch between installations or indeed what the dotnet binary actually is. For example, when I run the following:

which dotnet

I get the following path in my terminal:

So this is the current version of dotnet installed on my Mac which if I run dotnet --version gives me 1.0.0-preview2-003131. I'd recently upgraded from 1.0.0-preview1-002702 so where is that? I can see two installations in the following directories:


And each of these directories contains the following binaries which upon inspection look exactly the same as the binary returned by which dotnet:


To further compound my confusion when I execute either of the above binaries I get:

Microsoft .NET Core Shared Framework Host
Version : 1.0.1
Build : cee57bf6c981237d80aa1631cfe83cb9ba329f12

I get this when I just execute dotnet directly without any switches and when I append the --version suffix I get a blank line in my console. I (perhaps crudely) expected it to act the same as when I run the typical dotnet command and have no idea why the behaviour is different. I thought it might be something to do with a macOS installation but I fired up a couple of Linux VMs and had the same result.
September 18, 2016 22:32
Matthew - google hanselman global.json Dnvm and I have a blog post on that
September 19, 2016 0:05
One thing to consider with a self contained app is the target OS still needs the .NET prerequisites configured. On Ubuntu it means updating a few libraries using apt-get or curl in some cases and for OSX (if you are using the network)it means updating SSL.

The self contained model is very cool but after working for the past year to ship an app using it given the caveats above I am not sure it is really better for my users.
September 19, 2016 2:22
@Scott Hanselman

I can't believe I missed that - that's a lot clearer! Thanks :-)
September 19, 2016 3:09
Thanks for this article.

Re: "If I deploy my app along with .NET Core it's important to remember that I'll be responsible for servicing .NET Core and keeping it up to date"

What if I like the packaging model, but just want to use the same "curated" dependencies/patches that MS has spent time validating for FDD? Couldn't there be a simple tool for updating tested combinations of framework dependencies/patches? "Simple" not meant passive-aggressively...perhaps not so simple?
September 19, 2016 10:58
Thanks a lot for this article.

I was wondering whether there is a good official document explaining each section of the project.json file. It is clear to me now what the type="platform" is used for but what about other keys?

There are a lot of blog posts that are trying to explain these configuration keys but they either are out dated or didn't explain why.

September 19, 2016 15:06

I am very excited about .Net 5 ^H^H Core.

Will self-contained replace ClickOnce apps? If not, how would we use this in conjunction with ClickOnce? Will we use Certs?

How would we "servic[e] .NET Core and [keep] it up to date"? What about dependencies like SQL or local DB?

September 19, 2016 23:57
I would love to see some image encoder/decoder functionality added into .NET Core so that we can do cross-platform image processing and manipulation, even if just at the command line for now. Something along the lines of the "BitmapSource" class in WPF, or WIC in general. Is this being looked at?
September 20, 2016 12:38
It would be interesting to see if any of Xamarin's linking tech could reduce the size of the SCDs.
September 20, 2016 19:30
Will self-contained replace ClickOnce apps?
September 20, 2016 23:35
Well, it looks like we'll need separate binaries for win7, win8 an win10. Will win7 binaries work on win10?

Also I don't quite understand why did you not implement the scenario we have currently in full .Net - an exe bootstapper with app code. It could does the same thing as 'dotnet run', but wouldn't depend on external cli tools.
September 21, 2016 9:41
I published my app as self-contained app (on windows) for Ubuntu 14.04 but it couldn't run on Ubuntu!
September 26, 2016 17:08
The number of router producers using the IP as the default IP address for their routers is not really modest. This IP address belongs to factory options, but it can be updated, based on a user preferences. The default IP can easily be modified by using a router settings page. Generally, the IP is there to provide a distinctive identity for devices in the network system. This particular numeric Identity consists of 4 partitions of numerals, separated by dots. A particular IP range in which this IP sits is usually the one from to

Marie Wada
192.168.01 IP Address Definition
September 27, 2016 12:28
Looks to me a lot like what I used to do back in the days when I would write a C program, compile it and link it on whatever target platform, then distribute the "self-contained" exe. Looks like we've been through the full circle of .NET framework rediscovering the virtues of good old-fashioned linker... Just saying...
September 27, 2016 15:02
Applause :D
September 30, 2016 7:15
Is there a way we can compile for other Linux distro's/versions not listed currently?

I know with Mono (and other languages) if you can get a package for your distro and/or get Mono running on that distro from source you can create a static bundle right now for that target. Bit worried about this being too centralised and lagging behind current Linux changes.

In short if you can run it/build it it should also allow linking of .NET Core. Cross compiling to other platforms is an extra use case IF i want to deploy to other platforms - I would say a lot of places won't need to.
September 30, 2016 13:23
I can relate. Same here, I did Delphi/Pascal. Its like MS is selling old wine in new bags but its not quite the same.

Self contained apps is super nice but for .NET with its periodically security patches the developer gets responsible for security patches instead of the system administrator and the windows security patches! Yes thats whats a developer needs :-(

Applications compiled with for example c and pascal do not have all the security patches administration and responsibility which comes with .NET. I understand completely why MS does not want this too.

For this reason I can understand why a system administrator can refuse self contained Core apps.

For cross platform mobile and windows apps I switched back to Delphi Firemonkey (there is also a C++ builder Firemonkey) so I can compile directly without .core/mono (xamarin). Not only for security reasons btw.

October 01, 2016 3:05
Hi Scott,

When I try the above, it doesn't seem to generate any "EXE"s:

10/01/2016 08:52 AM <DIR> .
10/01/2016 08:52 AM <DIR> ..
10/01/2016 08:52 AM 441 scd.deps.json
10/01/2016 08:52 AM 4,096 scd.dll
10/01/2016 08:52 AM 408 scd.pdb 10/01/2016 08:52 AM 125 scd.runtimeconfig.json
4 File(s) 5,070 bytes
2 Dir(s) 2,299,387,904 bytes free

Tried on Mac and Windows. Would appreciate a pointer in the right direction ?

October 02, 2016 15:24
I know with Mono (and other languages) if you can get a package for your distro and/or get Mono running on that distro from source you can create a static bundle right now for that target. Bit worried about this being too centralised and lagging behind current Linux changes.
October 02, 2016 23:17
Is there a way we can compile for other </script> Linux versions not currently listed?

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.