Running ASP.NET 5 website on IIS

As ASP.NET 5 hit RC1 about a month go, more and more folks are looking at production deployments and other real life use cases of it. Going beyond demoware and experiments, to real applications, inevitably leads to thinking about hosting it somewhere – which, if your environment is Windows, most likely means IIS.

I have recently gone through quite some trouble getting it all to work, so I thought it might be useful to document and highlight some of the steps, which hopefully will save a bit of your time.

Adjusting your application

In order to be able to run on IIS, your application needs to include Microsoft.AspNet.IISPlatformHandler package. There is currently an 1.0.0-rc1-final version available which should be added as a dependency to your project.json.

You should then add the platform handler middleware to the Configure method in your Startup class:

If you are using the latest (RC1) ASP.NET 5 templates, then the ASP.NET 5 Web Application template already references the Microsoft.AspNet.IISPlatformHandler package and even has the above line of code in the Startup class by default.

Publishing your application

First of all you need to publish your application. If you are really old school, you may want to do it from Visual Studio and its classic “Publish” dialog, but it will not do anything magical – all it will do is just call into the command line dnu tool.

So instead, you can call it yourself from the folder of your web application (you can also pass the path to the web application project if you wanna invoke dnu from anywhere) and save yourself a hassle of even opening Visual Studio (or maybe you are on a Unix and you don’t even have it).

This command will publish your project and include the currently active runtime. You can obviously include a specific runtime too if that’s your intention, by passign its name. The publish command has plenty of other options such as for example specifying the source inclusion or the out path.

By default, the output path is bin/output in the same folder as your web application. The output can be xcopied to the server just like that.

The published source should have the following structure:

Screenshot 2015-12-15 22.47.21

Inside the approot folder there will be a web.cmd file which can be used to start your app. You can also start it by simply getting into wwwroot and calling dnx web. Of course IIS knows nothing about all this, so you’ll need some extra IIS setup to make it understand the external DNX process.

Setting up IIS

The prerequisite in IIS is that the HttpPlatformHandler module needs to be installed (minimum version 1.2). This component is nothing ASP.NET 5 specific – it simply allows process management for external processes that listen for HTTP requests and proxies requests into it; in this case it will be dnx.exe but it might as well be something like node.exe. You can install the latest handler using direct installer or WebPI from IIS download site here.

Once you have published your ASP.NET 5 app (previous step), you can proceed to setting up IIS.

Create a new application, and set the .NET CLR version on application pool to No managed code. We’ll be calling into dnx.exe to start your application, rather relying on the classic w3wp process.

Point your website to the wwwroot folder inside your publish output (previous step) location – or wherever you copied it to. If you run the application pool using the application pool identity, you have to make sure that IIS_IUSRS has access to your publish folder.

If you navigate to your site now and everything just works, then great – you can stop reading this post as your job is done, but at this point, chances are things will not be working yet.

That wwwroot has a web.config file inside which should at this point look like this:

You may want to set that stdoutLogEnabled=“false” to true immediately cause you’d want to get the errors, normally written to stdout of a hidden process, to be redirected to the log file.

Resolving errors

One possibility is that you see the following HTTP Error 500.19:

Screenshot 2015-12-15 23.06.12

This is because at the global config level, the system.webServer/handlers section is locked. To unlock it, go to IIS Manager, select your server root in the left navigation tree, then choose “Configuration Editor” > type system.webServer/handlers in the section selection dropdown and press enter. Then choose “unlock section” from the right action pane.

Screenshot 2015-12-15 23.33.40

Another (or next) potential issue that you may encounter, is that you see a blank page, that appears to be stuck loading forever. If that’s the case check the logs folder under the path defined in the web.config.

Most likely possibility is that dnx command is not being recognized. The reason for this is that the user used to run the IIS process doesn’t have it in the PATH. To combat this, you can do a few things:

  • change the application pool user to a one that has DNX on the PATH (i.e. your own user account)
  • add the DNX environment variables as a system-wide variables:
    • DNX_HOME, should point to your DNX folder, for me it’s C:\Users\filip\.dnx
    • DNX_PACKAGES, should point to your DNX packages folder, for me it’s C:\Users\filip\.dnx\packages
    • DNX_PATH, should point to your DNVM cmd file, for me it’s C:\Users\filip\.dnx\bin\dnvm.cmd

    If you choose this approach, you have to make sure that IIS_IUSRS has access to all the above folders too. Note that on IIS 10 you can also set environment variables specifically for the application pool.

  • instead of using …\approot\web.cmd to start up your application, you can also hardcode a path to dnx in the processPath attribute of the httpPlatform inside web.config. If you do that, you also need to pass the web argument in the arguments attribute

Finally, there might some other issues not mentioned here, that you can identify through the log file. For example, perhaps you provided a custom path to the dnx.exe but have not provided the arguments (“web”). This type of error would simply show up in the log as the usage help for dnx.

Overall, I tried this process on IIS 7.5, IIS 8 and IIS 10 – and at the end it ran successfully everywhere

Screenshot 2015-12-16 08.43.40

Be Sociable, Share!

  • Shahzad Hassan

    Thanks for the article. Just want to mention that the environment variables approach also works if you want to host the source location of the project without publishing. The IIS site should point to webroot (wwwroot folder or if you have specified a different one) of the project. In this case your web.config should look like

    https://github.com/tuespetre/dnx-watch-iis/blob/master/5-modify-web-config.png

    I had to modify the DNX_PATH environment variable to point it explicitly to dnx.exe of the rc1 runtime. dnvm.cmd command didn’t work for me. I am using IIS 7.5

    In order to take advantage of dnx watch command and experience save and refresh functionality you can create an additional environment variable called DNX_ARGS and set it to –project .. watch –dnx-args web. You would need to install the watch command using dnu commands install Microsoft.Dnx.Watcher and add the reference to Microsoft.Dnx.Watcher in project.json file.

    This is all documented at https://github.com/tuespetre/dnx-watch-iis/blob/master/README.md. This is for IIS 10 but I had to tweak it slightly for IIS 7.5

  • John Reilly

    Thanks for the awesome post Filip. It would be great if the ASP.Net 5 team released a “Ready IIS for ASP.Net 5″ utility. In enterprise situations it’s common practice (still) for developers to be kept at arms length from Production machines. This makes easing that initial deployment kind of a necessity. Maybe when when it hits RTM…

  • http://GudAnuf.com Bill Gerold

    Very Nice Post on hosting with IIS

    Have you Used Web API with CORS using the Latest bits? Would you consider writing a Post on it?

    When I have my site running from one URL – With the Web API and Client on the Same URL All works fine.

    Separating the Web API from the UI causes Errors

    Using what I have found on the web I have been able to get the GET verb working – Not having any luck with POST or PUT

    Thanks for all the great Posts

    Bill Gerold

    • Jason Rowe

      I’ve went through CORS configuration. I ended up using the CORS configuration in the web.config. I never could get the asp.net 5 CORS to work for me but I haven’t tried it in the latest version.

  • Dave Van den Eynde

    So let me get this right…

    The new ASP.NET 5 is cross platform, but we have to add a line to make it work on IIS?

    Am I the only one finding this a bit absurd?

    • smatthews1999

      Absurd and not happening…let me just go up to my ISP and make those changes to their servers.

      • Dave Van den Eynde

        yeah I was also wrong. That line is not to make it platform dependent, but rather to enable it to read the server features in case it was being run on IIS.

  • Jason Rowe

    I setup a brand new server 2012 instance and setup an ASP.NET 5 RC1-update website. The app pools is running as ApplicationPoolIdentity. I initially added IIS_User as you suggested but after the site was working I deleted the permission to see if it would break. Turns out I didn’t need to add permission for IIS_User and it runs fine without it. Do you happen to know why it would be needed? I’ve packaged the dnx up with the project folder if that matters. It seems the default permission are fine in my situation unless I’m missing something.

  • b.pell

    I deployed to IIS 7.5 and the log file just read a single line, “Access Denied”. The user running the AppPool had permissions to the site (in fact, we gave it full permissions just to test and it didn’t help). We then ran the app pool as a box administrator and everything worked great. When the admin manually started the web.cmd (to start the dnx.exe) under his credentials it worked great. The dnx seems to be accessing something outside of the site that it doesn’t have permission to (going to run procman to try to identify that tomorrow morning). I can’t seem to find documentation on what permissions need to be set and where. Obviously I can’t run it as an an admin, got any thoughts or advice?

  • plitwin

    Thank you for this post. I have to say the amount of mostly (totally?) undocumented work one has to do to publish to IIS is insane. Feels a little like Microsoft has disowned its old friends to make friends with the cool kids…

    • Rob Hill

      I agree, I’ve been at this crap now for nearly 2 days AND IT STILL DOESN’T WORK. Why is this so difficult @ RC1??????? WTF.

  • Shobhit Varshney

    Log file tracing below error when I publish my site on IIS 8.5 but it is working fine when running by w3wp and in browser I am getting 500 Internal Server Error: An error occurred while starting the application.

    Application startup exception: System.Exception: Could not resolve a service of type ‘TheWorld.Models.WorldContextSeedData’ for the parameter ‘seeder’ of method ‘Configure’ on type ‘TheWorld.Startup’.

    at Microsoft.AspNet.Hosting.Startup.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)

    at Microsoft.AspNet.Hosting.Internal.HostingEngine.BuildApplication()

    Hosting environment: Production

    Now listening on: http://localhost:12034

    Application started. Press Ctrl+C to shut down.

    Startup File: I have below code in my startup.cs file

    public void ConfigureServices(IServiceCollection services)
    {
    services.AddTransient();
    }
    public async void Configure(IApplicationBuilder app, WorldContextSeedData seeder, ILoggerFactory loggerFactory, IHostingEnvironment env)
    {
    await seeder.EnsureSeedDataAsync();
    }

    WorldContextSeedData file:

    using Microsoft.AspNet.Identity;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;

    namespace TheWorld.Models
    {
    public class WorldContextSeedData
    {
    private WorldContext _context;
    private UserManager _userManager;

    public WorldContextSeedData(WorldContext context, UserManager userManager)
    {
    _context = context;
    _userManager = userManager;
    }
    public async Task EnsureSeedDataAsync()
    {
    if(await _userManager.FindByEmailAsync(“varshneyshobhit98@gmail.com”) == null)
    {
    //Add User
    var newUser = new WorldUser()
    {
    UserName = “Shobhit”,
    Email = “varshneyshobhit98@gmail.com”
    };
    await _userManager.CreateAsync(newUser, “P@ssw0rd!”);
    }
    if (!_context.Trips.Any())
    {
    var usTrip = new Trip()
    {
    Name = “UsTrip”,
    Created = DateTime.UtcNow,
    userName = “Shobhit”,
    Stops = new List()
    {
    new Stop() {Name = “Atlanta, GA”, Arrival = new DateTime(2014, 6, 4), Latitude = 33.748995, Longitude = -84.387982, Order = 0 },
    new Stop() {Name = “New York, NY”, Arrival = new DateTime(2014, 6, 9), Latitude = 40.712784, Longitude = -74.005941, Order = 1 },
    new Stop() {Name = “Boston, MA”, Arrival = new DateTime(2014, 7, 1), Latitude = 42.360084, Longitude = -71.058880, Order = 2 },
    new Stop() {Name = “Chicago, IL”, Arrival = new DateTime(2014, 7, 10), Latitude = 41.878114, Longitude = -87.629798, Order = 3 },
    new Stop() {Name = “Seattle, WA”, Arrival = new DateTime(2014, 8, 13), Latitude = 47.606209, Longitude = -122.332071, Order = 4 },
    new Stop() {Name = “Atlanta, GA”, Arrival = new DateTime(2014, 8, 23), Latitude = 33.748995, Longitude = -84.387982, Order = 0 }
    }
    };
    _context.Trips.Add(usTrip);
    _context.Stops.AddRange(usTrip.Stops);
    _context.SaveChanges();
    }
    }
    }
    }

  • http://www.eivindgl.com/ Eivind Gussiås Løkseth

    “Hosting environment: Production
    Now listening on: http://localhost:14298” <— In case someone wants to host on port 80 in a production environment, here is a solution for that: http://stackoverflow.com/a/34438659/1421847 (See the app.Map("/ApplicationName", …) function, which can be modified in the Startup.cs file after deployment.)

  • Hui Zhao

    “HttpPlatformHandler module needs to be installed”, did you refer to the production server IIS or local IIS?
    Because I debug the ASP.NET 5 in Visual Studio 2015 the latest edition, everything is fine. I didn’t install the module.

  • http://rehansaeed.com Muhammad Rehan Saeed

    The easiest way to get things working is to ship the DNX runtime with the application. You can do this using the runtime argument when doing a dnu publish:

    dnu publish –configuration Release –no-source –runtime active –out Artifacts

  • Dean Reid

    Have you had any success with windows authentication with ASP.NET 5? I can get WindowsIdentity working in dev (via IIS Express) however, when I attempt to access the ClaimsPrincipal everything is empty or null.

  • Trevor Ward

    this post has been helpful twice now getting my API running on 2 different servers. Thanks!

  • http://www.becomingAdeveloper.com Kevin Neumann

    This was a really helpful article! I’m working on spinning up a new API using .NET Core, and I used this as a guide all the way through. I had the API up and running in IIS in no time…

    And then yesterday they released RC 2 and it all came tumbling down :(

  • Manoj Varma

    I want to change from IIS server to ASP.NET to host itself so that it can work on any platform but in beta version i used app.UseFileServer(); and changed some settings but I can’t find the function in latest version Can anyone help me?