Hosting An ASP.NET Core Web App As A Windows Service In .NET Core 3

Note, this tutorial is about hosting an ASP.NET Core web app as a windows service, specifically in .NET Core 3.

If you are looking to host a web app as a service in .NET Core 2, check out this other tutorial : Hosting An ASP.NET Core Web Application As A Windows Service In .NET Core 2

If you are looking to run a Windows Service as a “worker” or for background tasks, then you’ll want this tutorial : Creating Windows Services In .NET Core – Part 3 – The “.NET Core Worker” Way


This is actually somewhat of a duplicate of a previous post I did here. But that was using .NET Core 2+, and since then, things have changed quite a bit. Well… Enough that when I tried to follow my own tutorial recently I was wondering what the hell I was going on about when nothing worked for me this time around.

Why A Web App As A Windows Service

So this tutorial is about running a Web App as a Windows Service. Why would that ever be the case? Why would you not have a web app running under something like IIS? Or why a Windows Service specifically?

Well the answer to why not under IIS is that in some cases you may not have IIS on the machine. Or you may have IIS but it’s not set up to host .NET Core apps anyway. In these cases you can do what’s called a self contained deploy (Which we’ll talk about soon), where the web app runs basically as an exe that you can double click and suddenly you have a fully fledged web server up and running – and portable too.

For the latter, why a windows service? Well if we follow the above logic and we have an exe that we can just click to run, then a windows service just gives us the ability to run on startup, run in the “background” etc. I mean, that’s basically all windows services are right? Just the OS running apps on startup and in the background.

Running Our Web App As A Service

The first thing we need to do is make our app compile down to an EXE. Well.. We don’t have to but it makes things a heck of a lot easier. To do that, we just need to edit our csproj and add the OutputType of exe. It might end up looking like so :

<PropertyGroup>
  <TargetFramework>netcoreapp3.0</TargetFramework>
  <OutputType>Exe</OutputType>
</PropertyGroup>

In previous versions of .NET Core you had to install the package Microsoft.AspNetCore.Hosting.WindowsServices , however as of right now with .NET Core 3+, you instead need to use Microsoft.Extensions.Hosting.WindowsServices . I tried searching around for when the change happened, and why, and maybe information about differences but other than opening up the source code I couldn’t find much out there. For now, take my word on it. We need to install the following package into our Web App :

Install-Package Microsoft.Extensions.Hosting.WindowsServices

Now there is just a single line we need to edit. Inside program.cs, you should have a “CreateHostBuilder” method. You might already have some custom configuration going on, but you just need to tack onto the end “UseWindowsServices()”.

return Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        }).UseWindowsService();

And that’s all the code changes required!

Deploying Our Service

… But we are obviously not done yet. We need to deploy our service right!

Open a command prompt as an Administrator, and run the following command in your project folder to publish your project :

dotnet publish -c Release

Next we can use standard Windows Service commands to install our EXE as a service. So move your command prompt to your output folder (Probably along the lines of C:\myproject\bin\Release\netcoreapp3.0\publish). And run something like so to install as a service :

sc create MyApplicationWindowsService binPath= myapplication.exe

Doing the full install is usually pretty annoying to do time and time again, so what I normally do is create an install.bat and uninstall.bat in the root of my project to run a set of commands to install/uninstall. A quick note when creating these files. Create them in something like Notepad++ to ensure that the file type is UTF8 *without BOM*. Otherwise you get all sorts of weird errors :

The contents of my install.bat file looks like :

sc create MyService binPath= %~dp0MyService.exe
sc failure MyService actions= restart/60000/restart/60000/""/60000 reset= 86400
sc start MyService
sc config MyService start=auto

Keep the weird %~dp0 character there as that tells the batch process the current directory (Weird I know!).

And the uninstall.bat :

sc stop MyService
timeout /t 5 /nobreak > NUL
sc delete MyService

Ensure these files are set to copy if newer in Visual Studio, and now when you publish your project, you only need to run the .bat files from an administrator command prompt and you are good to go!

Doing A Self Contained Deploy

We talked about it earlier that the entire reason for running the Web App as a Windows Service is so that we don’t have to install additional tools on the machine. But that only works if we are doing what’s called a “self contained” deploy. That means we deploy everything that the app requires to run right there in the publish folder rather than having to install the .NET Core runtime on the target machine.

All we need to do is run our dotnet release command with a few extra flags :

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

This tells the .NET Core SDK that we want to release as self contained, and it’s for Windows.

Your output path will change from bin\Release\netcoreapp3.0\publish  to \bin\Release\netcoreapp3.0\win-x64\publish

You’ll also note the huge amount of files in this new output directory and the size in general of the folder. But when you think about it, yeah, we are deploying the entire runtime so it should be this large.

Content Root

The fact that .NET Core is open source literally saves hours of debugging every single time I work on a greenfields project, and this time around is no different. I took a quick look at the actual source code of what the call to UseWindowsService does here. What I noticed is that it sets the content root specifically for when it’s running under a Windows Service. I wondered how this would work if I was reading a local file from disk inside my app, while running as a Windows Service. Normally I would just write something like :

File.ReadAllText("myfile.json");

But… Obviously there is something special when running under a Windows Service context. So I tried it out and my API bombed. I had to check the Event Viewer on my machine and I found :

Exception Info: System.IO.FileNotFoundException: Could not find file 'C:\WINDOWS\system32\myfile.json'.

OK. So it looks like when running as a Windows Service, the “root” of my app thinks it’s inside System32. Oof. But, again, looking at the source code from Microsoft gave me the solution. I can simply use the same way they set the content root to load my file from the correct location :

File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "myfile.json"));

And we are back up and running!

22 thoughts on “Hosting An ASP.NET Core Web App As A Windows Service In .NET Core 3”

  1. Hi!
    Thank you for the post.

    I can run the application in console mode (Kestrel).
    When running as windows service, the service starts but I got the exception when navigating to some page:
    An unhandled exception occurred while processing the request.
    UriFormatException: Invalid URI: The format of the URI could not be determined.
    System.Uri.CreateThis(string uri, bool dontEscape, UriKind uriKind)

    Can you help me identify what this is happening?

    Reply
  2. Hello,

    I have tried to host web api as windows service, but i’ve faced the problem.. when im starting it as service error appears:
    ‘ Unable to configure HTTPS endpoint. No server certificate was specified, and the default developer certificate could not be found or is out of date.

    i have tried commands:
    dotnet dev-certs https –clean
    dotnet dev-certs https -t

    those command didnt solve my problems..
    maybe you have clue how i could solve this?
    Thanks in advance.

    thanks in advance

    Reply
  3. Hi.
    My application has several ports and all the ports are operational when run in console mode (Kestrel). But when I run it as a windows service, only the first port in the code is configured and am able to use it as well, but none of the other ports get set up. Request you to let me know how I could set up all the ports in a single windows service.

    Thanks in advance!

    Reply
  4. This doesn’t work for me. I’ll get Error 1053: The service did not respond to the start or control request in a timely fashion.

    Reply
    • Create a small POC and upload it to Github and I can take a look for you. Otherwise it’s a little hard to guess what the issue might be.

      Reply
  5. Hi

    Thanks for this. My web application is running as a windows service and I can browse the different pages however the content in the wwwroot isn’t accessible i.e. files like the site.css and bootstrap.min.css.

    For instance when the home page loads, in Chrome Developer Tools the request http://localhost:5000/lib/bootstrap/dist/css/bootstrap.min.css fails with a 404 not found error.

    Running the application executable normally works fine. Any ideas on how to work around this?

    Reply
    • Hi Amit,

      Theoretically you “kinda” could. It depends what you are trying to do but yes you can start a web server from another application since it’s all just really a “console app” these days.

      However I don’t think it would work out of the box. You would probably have to refactor some pieces to be called both from your other project, and running solo (If you wanted that).

      Reply
      • Good question, Govardhana, I have been ‘forced’ into using this command line kestrel approach since IIS doesn’t seem to work with my .net 5 core web api (issues with web.config that I can’t fathom).
        My client (and my browser) just reports back ‘connection to the server could not be established’ and I’m losing the will to live!!

        Reply
  6. I cannot thank you enough for this simple yet remarkably clear explanation which has just saved my literally **hours** of head-scratching and reading, given that .NET Core is all new to me 🙂

    Reply
  7. Thanks so much for this. I now am able to access an assets folder that was not found by the app when running as a service. Great post.

    Reply

Leave a Comment