IIS Performance Tips and Scripts

By Nate Graham On July 15, 2020

IIS Performance Tips and Scripts
Lately, I’ve been trying to improve the performance of our web server, which runs IIS 10.0 on a Windows Server 2019 virtual machine. We host over 150 application pools that run many of our internal sites and tools, as well as some of our multi-tenant applications. In this article, I’ll give a few tips on how I was able to reduce the server’s memory footprint by 50% and improve the responsiveness of our sites.

Configuration

IIS configuration can be a bit confusing. There is a hierarchy of configuration files that handle various settings, and it can be tough to memorize which settings are located in each one. To break it down:
  1. At the root level, the machine.config and root web.config mainly handle the .NET Framework assemblies available to your hosted applications. 
    • These files are located in C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config
  2. Next is the applicationHost.config, which handles most of the things you see in IIS Manager, such as site definitions, bindings, application pools, virtual directories, etc. 
    • This file is located in C:\Windows\System32\inetsrv\config 
    • Whenever you make a change to this file, a backup is automatically created in C:\inetpub\history under a “CFGHISTORY_” folder. This has saved me a few times!
  3. The last level of configuration is done on an individual site basis with the web.config file that is located in your site folder.
Luckily Microsoft has a handy Powershell module available for IIS 8 and above to help manage all these settings. I will demonstrate using the IISAdministration module. You can verify it is installed with this snippet:
if (-not (Get-Module IISAdministration)) {
    Install-Module -Name IISAdministration
}

Site Responsiveness

By default, your IIS site is not actually loaded and running until the first request is served. This means that your site can be slow to load initially. There are a few different strategies you can employ to mitigate this, but I chose to have our sites ready and running at all times. To do this, I will be setting some properties in the applicationHost.config file using Powershell.

I first need to make sure I have the IISAdministration module loaded, and get a reference to the IIS Server Manager:
Import-Module IISAdministration
$manager = Get-IISServerManager
Next, I iterate through all of the sites and set the properties I want:
# Alternatively, get an individual site using: $site = $manager.Sites["My Site Name"] or Powershell pipeline $manager.Sites | Where-Object ...
foreach ($site in $manager.Sites) {    
    # Set the site to automatically start when Windows is started    
    # Maps to applicationHost.config node: <sites> <site serverAutoStart="true">
    $site.ServerAutoStart = $true

    # Get a reference to the root site application using its path “/”. Maps to applicationHost.config node: <sites> <site name="My Site Name"> <application path="/">
    $siteRootApplication = $site.Applications["/"]

    # Sets <application path="/" preloadEnabled="true"> to make IIS simulate a request to your site when it's application pool starts up
    $siteRootApplication.SetAttributeValue("preloadEnabled", $true)

    # Sets <application path="/" serviceAutoStartEnabled="true"> to make IIS use the default Application Initialization warmup module.
    $siteRootApplication.SetAttributeValue("serviceAutoStartEnabled", $true)    

    # Get a reference to the associated application pool. Maps to applicationHost.config node: <applicationPools> <add name="My App Pool Name">
    $appPool = $manager.ApplicationPools[$siteRootApplication.ApplicationPoolName]

    # Sets <add name="My App Pool Name" autoStart="true"> to make the app pool start automatically when the server starts
    $appPool.AutoStart = $true

    # Sets <add name="My App Pool Name" startMode="AlwaysRunning"> to make the w3wp.exe worker process always running
    $appPool.StartMode = "AlwaysRunning"
}

# Need to commit changes for the applicationHost.config file to be updated
$manager.CommitChanges()

Now our applicationHost.config file will be updated with the above values, and our sites and application pools will automatically startup with the server. Our sites will be speedy and have no delay when the first request comes in, but now it seems we have another problem. With all of these sites always running, it looks like the server’s memory is filling up fast! What do we do?

Reduce Memory Usage

By default, IIS will configure application pools as 64-bit when the operating system supports it, and this can be a problem for site performance and server memory usage.

It turns out that Microsoft recommends setting your application pools to 32-bit unless you have a specific 64-bit dependency, and it has been the recommendation since 2007! It seems this advice continues into .NET Core as well. From the article:
One of the performance benefits of the x64 platform is that it increases virtual address space, making more memory available. We recommend that you configure IIS to use a 32-bit worker processes on 64-bit Windows. Not only is compatibility better than the native 64-bit, performance and memory consumption are also better.
To do this, we have to set the enable32BitAppOnWin64="true" attribute on our application pools. We can do this in the same Powershell snippet above by adding this line below the other application pool configuration changes:
$appPool.Enable32BitAppOnWin64 = $true
However, since we probably want this setting to apply to all of our application pools, and all the ones we make in the future, we should set this on the <applicationPoolDefaults> node in applicationHost.config. To do this with Powershell, we can add this to our snippet before we call CommitChanges():
$manager.ApplicationPoolDefaults.Enable32BitAppOnWin64 = $true
After the changes are committed, our node will be updated like this:
<applicationPoolDefaults enable32BitAppOnWin64="true">
Note that we could also set the StartMode and AutoStart properties on the ApplicationPoolDefaults as well.

In our situation, our server was consistently utilizing around 80% memory, according to Task Manager. After setting the application pool defaults to enable 32-bit, that utilization IMMEDIATELY dropped to 40%. Quite a difference!

Conclusion

I hope this article was able to help you improve your site performance with IIS, or share some ideas for utilizing Powershell for site configuration. The IISAdministration module is very powerful and can be used to manage all of your site settings well beyond what I’ve covered here. Please see the documentation for additional details.

Additional references: 

Share This Post:

Twitter Pinterest Facebook Google+
Click here to read more Tutorials posts
Start a Project with Us
Photo of the author, Nate Graham

About the author

Nate first started using a computer at 5 years old when his grandfather showed him how to type DOS commands to play games. Ever since then he’s had a desire to learn new things about technology and software development. At home, Nate enjoys playing video games with his son, Carson, dancing with his daughter, Sutton, and laughing with his wife, Ashtyn.

View other posts by Nate

Subscribe to Updates

Stay up to date on what BizStream is doing and keep in the loop on the latest with Kentico.