Brosteins

Developers, Technology Evangelists, Bros.

Setting up the Hangfire Dashboard in a Windows Service

I’ve started a side project recently that involves creating a Windows Service to host a Hangfire server and dashboard (if you’re not familiar with Hangfire, check out Nick’s post or head over to hangfire.io). The Hangfire dashboard is a website, but I was hosting it within a Windows Service. The Hangfire documentation does a great job of explaining everything you need to do to get running within a Windows Service; however, I ran into a few roadblocks because my Windows Service was running as a service account rather than NETWORK SERVICE. In this post, I’ll walk through the basics of setting up the Hangfire dashboard in a Windows Service, including the gotchas you’ll need to account for when installing the Windows Service with a service account.

Configure the Hangfire Dashboard in a Windows Service

To get started, you’ll need create a Windows Service project in Visual Studio. If you haven’t done this before, check out this article on MSDN.

Let’s assume you now have a Windows Service project, in order to tell Hangfire to run it’s dashboard from within the service, create a class (mine is named Startup) and add the following

  • references to Hangfire and OWIN
  • an OwinStartup attribute
  • and a call to the UseHangfireDashboard() method of your IAppBuilder object

The OwinStartup attribute is used to mark which class in an assembly should be used for automatic startup, and UseHangfireDashboard() tells Hangfire that it should host the hangfire dashboard.

After creating this class, update your service startup code. You’ll need to add a reference to Hangfire and Microsoft.Owin.Hosting. You’ll also need to tell Hangfire where where to host the persistent job store, and tell OWIN how to host the Hangfire dashboard.

By calling, GlobalConfiguration.Configuration.UseSqlServerStorage(“hangfire”), Hangfire is configured to use SQL Server as the persistent job store, using the connection string named “hangfire”. I then configured the OWIN startup options to initialize hosting on three different URLs (localhost:9095, 127.0.0.1:9095, and the current server’s machine name on port 9095). Finally, the call to WebApp.Start, ties everything together (the Startup class that registers Hangfire’s dashboard and the startup options for used for hosting).

Pretty simple.

Run the Hangfire Windows Service as NETWORK SERVICE

At first, I decided to run the Hangfire Windows Service as NETWORK SERVICE. This helped me verify the service was configured properly. Browsing to http://localhost:9095 on the server launched the Hangfire dashboard.

hangfire dashboard hosting in a windows service

For a variety of reason I won’t go into here, I didn’t really want my service running under this security context. So, I reinstalled the service using a domain service account.

Run the Hangfire Windows Service as a Domain Service Account

Once I decided to run the Hangfire Windows Service as a Domain Service Account, I ran into a problem – I could no longer access the dashboard. After a brief search, I realized that I needed to reserve the collection of service URLs (http://localhost:9095, http://127.0.0.1:9095, and http://{machinename}:9095) to be used by the domain service account. The Windows command “netsh” is the tool for this job.

Per the MSDN documentation, “netsh http add urlacl” reserves the specified URL for non-administrator users and accounts. The discretionary access control list (DACL) can be specified by using an account name with the listen and delegate parameters or by using a security descriptor definition language (SDDL) string.

I added the following commands to my auto deployment PowerShell scripts.

If you look closely at the above PowerShell script, you’ll notice the use of $env.computername. This is a PowerShell Windows environment variable that is commonly used in scripts; however, I wanted to call attention to it because I have it embedded within a URL that also has a port number specified. Originally, I had http://$env.computername:9095/ in my script; however, PowerShell thought that the :9095 was part of the environment variable. To tell PowerShell this is not a continuation of the environment variable, I had to escape the colon with a grave (a.k.a. grave accept, found underneath the tilde ~ character).

Share

14 comments for “Setting up the Hangfire Dashboard in a Windows Service

  1. Avatar
    Steven M Hoff
    June 7, 2016 at 12:33 pm

    Thanks for this information, I was really scratching my head using just the official hangfire documentation.

    • Avatar
      June 13, 2016 at 11:26 am

      Thanks, Steven. We’ve been using Hangfire for a while, and love it. What are you using Hangfire for?

  2. Avatar
    Terry Aney
    January 13, 2017 at 4:19 pm

    Trying to follow your examples. I have a service running as a domain account. I added Hangfire.Core and Hangfire.SqlServer and everything compiled, so I was then attempting to see the dashboard.

    1. I added:

    2. Added the Startup.cs class you mentioned.

    3. Added the WebApp.Start code into OnStart() method you mentioned.

    4. I ran the netsh commands from an elevated command prompt.

    5. Started the service and attempted to browse to http://127.0.0.1:9095/ but I received a 404.

    Few notes:

    My Sql db is LocalDB, therefore I can never run the service as a local account (thus not able to initially verify anything on system account).

    I don’t have IIS installed on my development box. This would be my first OWIN application, but from what I understand, that shouldn’t be an issue.

    Any suggestions of places to look for why my machine will not serve up the dashboard?

    • Avatar
      January 19, 2017 at 12:37 pm

      Hi, Terry. Could you put your implementation on Github somewhere, where I can run and test?

      Perhaps you can try running Hangfire with in-memory storage instead of LocalDB just to ensure everything is configured properly first. Something else I failed to mention in my post is that I no longer create windows service projects in Visual Studio, instead, I use a package named Topshelf: http://topshelf-project.com/, which allows you to easily configure a Console app as a service, eliminating the hassle of debugging and running windows services. With Topshelf, you press F5 in your dev environment and it just works.

      • Avatar
        Terry Aney
        January 19, 2017 at 3:27 pm

        I’ve heard of Topshelf before, but I had my service written already, so didn’t back out. It would be bit nicer than attaching VS to the running service. Have you migrated a service to console/Topshelf? Any gotchas there?

        As for a sample implementation, I could maybe write a simple windows service and try to integrate it…don’t want to burden you with my current application as it has more dependencies. But I can tell you this, I deployed the service to a ‘real sql server’ machine and Hangfire is working properly, it’s just the dashboard I can’t get to work.

        In my deployment:
        a) The service is running as an local admin user
        b) I ran the netsh add commands you specified

        Questions:
        a) Is there a command I can run to see the list of configured ‘netsh add’ commands that have been run? I couldn’t find a ‘list’ command.
        b) Know of any possible firewall issues? (couldn’t imagine any since I’m running on the same machine WebApp is running on)
        c) Is there a way to confirm that WebApp.Start() ‘really’ worked? My code is below and successfully runs.

        var options = new StartOptions();
        options.Urls.Add( “http://localhost:9095” );
        options.Urls.Add( “http://127.0.0.1:9095″ );
        options.Urls.Add( $”http://{Environment.MachineName}:9095” );

        WebApp.Start( options );

        Thanks for your time. Let me know if I can provide anything else or if you think a sample service would be the next step.

        • Avatar
          January 22, 2017 at 3:13 pm

          Terry –

          Thanks again for the questions. To answer your first question, there is a netsh command parameter to list urlacl’s currently registered. Use: netsh http show urlacl. But, I suspect this isn’t the root cause of your issues.

          I’ve placed an example windows service out on GitHub that you can use to compare to your version: https://github.com/mikebranstein/HangfireWindowsServiceExample.

          In creating this example service, I re-discovered a few issues that I think could shed some light on exactly what you’re encountering:

          First, browsing to http://127.0.0.1:9095 will return a 404, because the Hangfire dashboard is auto-configured to run off of the /hangfire URL route. Try navigating to http://127.0.0.1:9095/hangfire. If you’re still running into issue, read on. If this solves your issue, don’t shoot yourself in the foot – after running my service code the first several time, I kept receiving a 404 error. I sat staring at the screen for about 20 minutes before I realized I had forgotten the /hangfire route.

          If you’re still having issues, it’s likely a permissions or mis-configuration issue. Check out the event log on your computer by using the Event Viewer application. The Application event log will report any exceptions thrown. Look for a red X next to the name of your service. This will lead you down the right path quickly. Here’s a few issues you may run into:

          Service cannot be started. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.Net.HttpListenerException: Access is denied

          This issue is likely a permissions-related issue and the service account doesn’t have permissions to run/host a website on the port. The solve this, use the netsh command.

          To further diagnose this issue, I recommend temporarily switching the service account to run as Local System, which has the appropriate permissions to run/host a website on the designated port without running the netsh command. Once you’ve validated the hangfire dashboard runs as Local System, then you can start troubleshooting other configurations.

          Service cannot be started. System.MissingMemberException: The server factory could not be located for the given input: Microsoft.Owin.Host.HttpListener

          I should have mentioned this int he first article, but you also need to install the Microsoft.Owin.Host.HttpListener NuGet package in order for Owin to host a website via a windows service. You don’t need to do anything other than include the NuGet package and recompile to resolve this error.

          Service cannot be started. System.ArgumentException: Could not find connection string with name ‘hangfire’ in application config file

          This is an easy one – be sure there’s a connection string named “hangfire” in the application configuration file. In the example service I included, I avoided this issue by installing the optional NuGet package, Hangfire.MemoryStorage, and told hangfire to use memory storage instead of SQL server. Check out the code here: https://github.com/mikebranstein/HangfireWindowsServiceExample/blob/master/ExampleService/Service1.cs.

          If you’re still having trouble, post the code and I’ll help you troubleshoot further.

          • Avatar
            Terry Aney
            January 24, 2017 at 1:09 pm

            http://127.0.0.1:9095/hangfire was the issue. Thanks for taking the time to look into it!

          • Avatar
            Terry Aney
            January 24, 2017 at 1:24 pm

            One question looking at your sample code:

            I don’t have these packages installed. Were they just you trying to debug problem before you figured out what it was?

            I also see you didn’t use Topshelf. You didn’t get a chance to answer what the process was like migrating an existing service into Topshelf, and if it is worth it if you have the service already installed and working. Curious on your opinion, going to look at Topshelf again now.

          • Avatar
            Terry Aney
            January 24, 2017 at 1:42 pm

            My one last question…if the server running Hangfire and dashboard are on my network, can I hit the url like:

            http://192.168.100.14:9095/hangfire

            After adding following to code:

            options.Urls.Add( “http://192.168.100.14:9095” );

            I seem to get a 500 error (same thing if IP is the machine name instead). Don’t know where to look for errors and nothing is showing up in trace.

      • Avatar
        Terry Aney
        January 19, 2017 at 3:32 pm

        Forgot to say that there is no IIS on the deployment machine either.

  3. Avatar
    Daniel
    January 18, 2017 at 1:27 pm

    Thanks for that. I was considering doing it in a virtual directory but probably this way is easier.

    Do you know how to do any customizations to the dashboard like adding extra features?

    • Avatar
      January 19, 2017 at 12:38 pm

      Thanks, Daniel. I do not believe you can add customized components to the dashboard.

  4. Avatar
    pregunton
    March 11, 2018 at 8:11 am

    Hangfire Client configuration ?

  5. Avatar
    wissem
    August 22, 2018 at 5:15 am

    hello,
    is it the same for Linux Machine ?
    Thank you.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.