Hosting ASP.NET Core Applications on IIS – A Detailed Look

In last few years, I spent a significant amount of time researching, writing, speaking on ASP.NET application’s performance. Performance of a web app is not just its code but it depends a lot on the hosting platform, configurations and the usage of available resources etc. If you are hosting ASP.NET Webforms/MVC application on IIS, then you can follow below tips and get benefitted quickly.

12 tips to increase the performance of your ASP.NET application drastically – Part 1
12 tips to increase the performance of your ASP.NET application drastically – Part 2

Whenever I think about the performance of any web application, there are three major areas comes into my mind (I am not considering the external factors)

  1. Hosting Server
  2. Application itself
  3. Database/Third Party APIs etc

Any application can’t be highly performant until all three are properly optimized. It can perform better if all were taken in consideration in early stages development lifecycle.

ASP.NET Web Forms is/was very popular web framework but it has many known performance issues. To overcome many and to use latest standards and best practices, Microsoft introduced ASP.NET MVC framework. Over time, Microsoft sensed the need of new highly performant cross platform web framework to compete in market and introduced the all new ASP.NET Core framework (Initial Name: ASP.NET 5) which is written from scratch. Although at a high level, most of the constructs are similar with ASP.NET MVC but the underlying engine got completely rewritten.

While working on ASP.NET Core, MS worked on the complete stack, like for backend – they introduced Entity Framework core and for hosting the application, a new highly optimized web server, known as Kestrel. But as we know IIS provides an array of features, configuration and battle tested for all kind of scenarios, Kestrel was not ready as an edge server.

Although the new server was introduced with ASP.NET Core but it appears that initially, the main focus was on ASP.NET core and other backend frameworks. During ASP.NET 1.X, it was advised to used IIS as frontend server which in turn forwards the requests to Kestrel. A reverse proxy was suggested due to security and reliability reasons. It didn’t have defense against attacks and other configurations like various timeouts, size limits, concurrent connection limits etc. So, we only had the option to deploy the application using IIS (Nginx, Apache for other platforms) as reverse proxy. In the newer versions (ASP.NET Core 2.X), lot of enhancements has been made in Kestrel and with ASP.NET Core 2.1 and later, Kestrel started supporting https (which is nowadays basic requirement for hosting any web application on internet). Now it can be used as internet facing server . In this post and coming post, we will discuss the available deployment options with IIS and explore that what is happening behind the scene.

Using IIS as a Reverse Proxy:

In this scenario, ASP.NET Core application is hosted on kestrel which sits behind IIS. At a high level, it looks like

 

 

 

I created an asp.net core sample application which I deployed on IIS. Let’s take a quick look into the steps for deployment.

  1. First, we need to configure IIS on Windows (if it’s not there).
  2. Install the .NET Core Hosting Module (Can be downloaded from here based on the versions).  After installation, you can go to the IIS modules section and ANCM will appear as
  3. Create the website at IIS
    1. Create a folder which will contain app’s published folders, files and binaries
    2. Create a new logs folder inside the earlier created which will contain the logs created by ASP.NET Core module when stdout is enabled.
    3. In IIS, Add a new Website by right clicking on Sites folder under Connections -> ServerName as NetCoreProxy (say).
    4. It by default creates a new application pool named as preferably -. Go to application pools under Connections-> Server Name and Click on NetCoreProxy. Select “No Managed Code” under .NET CLR version 
      “No Managed Code” why? We will discuss it later.
    5. Check the identity which is by default as “AppPoolIdentity” and change it if needed.
  4. Last step, publish the website by Right Clicking on the project in Visual Studio in the newly created folder at 3 a.

Publishing the ASP.NET Core App

While publishing an ASP.NET Core app, we need to select the deployment mode option which has two options: Framework dependent and Self-Contained. Publishing with second option produces a significantly larger binaries because it contains the coreclr and other required system libraries. This should not be a preferred option until necessary because of the huge size and performance implications as it loads all the resources and JIT them on the fly.

Now we should be able to browse our ASP.NET core web site.

Let’s take a look in details

The first question that arises is why do we need to install something on IIS? One of the problems with earlier versions of ASP.NET, that ASP.NET and IIS both has its own pipeline which contains multiple modules (IIS pipeline contains native and managed modules) and each request has to go through both the pipelines invoking each module (However this problem was resolve till certain extent using Integrated Pipeline setting at IIS). Most of the times, many of them are not used by the appliccation. Using ASP.NET Core, we got a new pipeline which has list of middleware that are added at application startup based on the need. To avoid all the IIS overheads, when we install the bundle on IIS, it adds a native module AspNetCoreModule (ANCM) which is invoked at very early stage and forwards the requests to Kestrel as soon as it reaches to IIS. Earlier it was done by an existing module called HTTP Platform Handler which used to forward the request but MS decided to create a new native module ANCM which was fork of HTTP Platform Handler to have better control and able to provide tailored features.

Also, we have seen in deployment steps that we selected “No Managed Code” while configuring the application pool, it means we don’t want to run any managed modules for the requests of this application. As mentioned earlier, IIS pipeline contains the native and managed modules both, ASP.NET core requests do go through some of the native modules like authentication modules (Anonymous, basic, Windows etc), dynamic compression etc. Few native modules are not part of the process and as many of them has a corresponding asp.net core middleware (for details, look here). Let’s take a look that how does it work

We can see here that ASP.NET core app runs into a different process dotnet.exe, not in the worker process which was the case with earlier versions of ASP.NET. I also used the process explorer to see it

Here we can see that dotnet.exe is a different process which runs the application. What is this Console Host process? If we take a look in ASP.NET core’s startup class, we will see a main method, similar to console application which is the first method which gets called when the application starts.

Let’s go through step by step process, how a request is served in this scenario

  1. The request is received by the HTTP.sys from the network.
  2. If response is cached at HTTP.sys then it is sent back from there else gets a place the corresponding Application Pool’s queue.
  3. When a thread is available in the thread pool, it picks up the request and start processing it.
  4. The request goes through IIS processing pipeline. As mentioned earlier the request goes through few native IIS modules and once it reaches to ANCM, it forwards the request to Kestrel (under dotnet.exe).
  5. ANCM has a responsibility to manage the process as well. If (re)starts the process (if not running or crashed) and IIS integration middleware configure the server to listen the request on port defined in environment variable. It only accepts the requests which originates from ANCM.
    Note -Please do note that in ASP.NET Webforms/MVC the application is hosted under the worker process w3wp.exe which is managed by Windows Activation Service (WAS) which was part of IIS.
  6. Once the request is received by Kestrel, it creates the HTTPContext object and request is handed over to ASP.NET Core middleware pipeline.
  7. The request is passed to routing middleware which invokes the right controller and action method (model binding, various filters almost similar way as earlier versions).
  8. Finally, the response is returned from the action and passed to kestrel via Middlewares and later sent back to client via IIS.

So we can see the request processing is quite different than earlier versions ASP.NET apps hosted on IIS.

Using Kestrel as an Edge Server

With ASP.NET Core 2.1, Kestrel got lots of new capability which makes it capable of using it an edge server, however for the enterprise level application, it is still recommended to use it behind a proxy. Obviously, one of the first choices is using IIS in windows environment. With ASP.NET Core 2.2, It got some more refinement which makes IIS a better option. We will discuss that in next blog post. Kestrel as an edge server looks like

As this post is specific to IIS hosting, I will not go into the detail but earlier lots of important web server features like Keep alive timeouts, connection timeout, body reading timeouts, request timeouts, size limits, concurrent connection limits, memory limits etc were not available but now most of the configuration can be done and it supports https as well. Even with that Microsoft suggests using IIS as a reverse proxy due to additional feature, security, configurations and many more.

Hope you have enjoyed the post. Do share the feedback. In next post, We will discuss the In-process enhancement  in ASP.NET Core 2.2 in detail.

Cheers
Brij