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.
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:
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
}
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:
$site.ServerAutoStart = $true
# Get a reference to the root site application using its path “/”. Maps to applicationHost.config node:
$siteRootApplication = $site.Applications["/"]
# Sets to make IIS simulate a request to your site when it's application pool starts up
$siteRootApplication.SetAttributeValue("preloadEnabled", $true)
# Sets 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:
$appPool = $manager.ApplicationPools[$siteRootApplication.ApplicationPoolName]
# Sets to make the app pool start automatically when the server starts
$appPool.AutoStart = $true
# Sets 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?
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:
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!
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:
We love to make cool things with cool people. Have a project you’d like to collaborate on? Let’s chat!
Stay up to date on what BizStream is doing and keep in the loop on the latest in marketing & technology.