What Is an IIS Application Pool?

An application pool in IIS is an isolated worker process environment that hosts one or more web applications. Each application pool runs as one or more instances of w3wp.exe (the IIS worker process). Application pools provide process isolation: if one application crashes or consumes excessive memory, it does not affect applications running in separate pools. On Windows Server 2022, IIS 10 continues to support both managed (.NET CLR) and unmanaged (native) application pools, and every application must be assigned to exactly one pool.

Understanding and correctly configuring application pools is fundamental to running a stable, performant, and secure IIS environment. This guide covers pool creation, identity configuration, recycling settings, process model tuning, CPU throttling, pipeline modes, and isolation strategies.

Creating Application Pools with PowerShell

The WebAdministration PowerShell module provides the New-WebAppPool cmdlet for creating application pools from the command line or scripts:

# Import the WebAdministration module (auto-loaded on IIS servers, but explicit is safer)
Import-Module WebAdministration

# Create a new application pool
New-WebAppPool -Name "MyApp_Pool"

# Verify it was created
Get-Item "IIS:AppPoolsMyApp_Pool"

To assign a web application to the new pool:

Set-ItemProperty "IIS:SitesDefault Web Sitemyapp" -Name applicationPool -Value "MyApp_Pool"

For a full creation-and-assignment workflow with all settings specified at creation time, use Set-WebConfigurationProperty against applicationHost.config:

Add-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools" `
  -name "." `
  -value @{
    name = "MyApp_Pool"
    managedRuntimeVersion = "v4.0"
    managedPipelineMode = "Integrated"
    enable32BitAppOnWin64 = "false"
    startMode = "AlwaysRunning"
  }

Application Pool Identity Types

The identity under which a worker process runs determines what Windows resources it can access: file system paths, registry keys, network resources, and COM objects. IIS provides four built-in identity types plus the ability to run under a custom service account.

LocalSystem — The highest-privilege built-in account. Has full access to the local machine and can access network resources using the computer account. Never use this for web applications; a compromised application running as LocalSystem has complete control over the server.

LocalService — A limited built-in account with local machine privileges reduced compared to LocalSystem. Has minimal network access. Rarely used for IIS pools.

NetworkService — The legacy default for IIS application pools in older versions. Has limited local privileges and accesses network resources using the computer account. Has more privilege than needed for most web apps; avoid on new deployments.

ApplicationPoolIdentity — The recommended default since IIS 7.5. IIS automatically creates a virtual account named IIS AppPool<PoolName> for each pool. This account has no local rights beyond what is explicitly granted, cannot log on interactively, and is automatically cleaned up when the pool is deleted. This is the most secure option for most scenarios.

Custom Account — Allows you to specify a domain or local user account. Use this when the application needs specific Windows permissions (e.g., read from a network share, write to a database using Windows Authentication).

# Set identity to ApplicationPoolIdentity (most secure, recommended)
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/processModel" `
  -name "identityType" -value "ApplicationPoolIdentity"

# Set identity to a custom domain account
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/processModel" `
  -name "identityType" -value "SpecificUser"

Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/processModel" `
  -name "userName" -value "DOMAINsvc_webapp"

Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/processModel" `
  -name "password" -value "P@ssw0rd123!"

When using ApplicationPoolIdentity, grant the virtual account access to directories it needs by using its virtual name:

# Grant read access to web content directory
icacls "D:WebAppsmyapp" /grant "IIS AppPoolMyApp_Pool:(OI)(CI)R" /T

# Grant write access to a logs directory
icacls "D:WebAppsmyapplogs" /grant "IIS AppPoolMyApp_Pool:(OI)(CI)M" /T

Recycling Settings

Application pool recycling causes IIS to restart the worker process, clearing any accumulated memory leaks or stuck threads. IIS supports several recycling triggers, and multiple triggers can be active simultaneously. The default configuration recycles the pool every 1740 minutes (29 hours).

Time-based recycling — Recycles after the worker process has been running for a specified number of minutes:

Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/recycling/periodicRestart" `
  -name "time" -value "00:00:00"  # Set to 00:00:00 to disable time-based recycling

Request-based recycling — Recycles after a certain number of requests have been served. Useful to mitigate memory leaks in applications that accumulate state over many requests:

Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/recycling/periodicRestart" `
  -name "requests" -value "50000"

Memory-based recycling — Recycles when the worker process reaches a virtual or private memory threshold (in kilobytes):

# Recycle if virtual memory exceeds 1 GB
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/recycling/periodicRestart" `
  -name "memory" -value "1048576"

# Recycle if private bytes exceed 512 MB
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/recycling/periodicRestart" `
  -name "privateMemory" -value "524288"

Scheduled recycling — Recycles at specific times of day, ideal for applications that can handle a brief interruption during low-traffic hours:

Add-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/recycling/periodicRestart/schedule" `
  -name "." -value @{value="03:00:00"}

Process Model Settings: Idle Timeout and Max Processes

The process model section controls how worker processes behave over their lifetime.

Idle timeout — The amount of time a worker process can remain idle before IIS shuts it down to conserve resources. The default is 20 minutes. For applications where cold start is expensive (e.g., .NET applications with long startup times), either increase this value or set startMode to AlwaysRunning:

# Set idle timeout to 0 to disable (process never shuts down due to inactivity)
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/processModel" `
  -name "idleTimeout" -value "00:00:00"

# Or set to 1 hour
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/processModel" `
  -name "idleTimeout" -value "01:00:00"

Max worker processes — Enables a Web Garden, where multiple worker processes serve the same application pool simultaneously. This can improve throughput on multi-core servers for CPU-bound applications, but breaks in-process session state. Only use this with session state stored externally:

Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/processModel" `
  -name "maxProcesses" -value "4"

Startup time limit — The maximum time IIS allows for the worker process to complete initialization. Increase this for apps with slow startup:

Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/processModel" `
  -name "startupTimeLimit" -value "90"  # seconds

CPU Throttling

CPU throttling prevents a misbehaving application pool from consuming all available CPU cycles and starving other pools. Configure a CPU limit and action:

# Limit pool to 50% of one CPU (value is in 1/1000ths of a percent, so 50000 = 50%)
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/cpu" `
  -name "limit" -value "50000"

# Action when limit is reached: Throttle (reduce priority) or KillW3wp (terminate)
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/cpu" `
  -name "action" -value "Throttle"

# Reset interval for CPU measurement
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']/cpu" `
  -name "resetInterval" -value "00:05:00"

The Throttle action reduces thread priority so other processes get more CPU time. KillW3wp terminates the worker process when the CPU threshold is exceeded — use this only as a last resort protection against runaway processes.

.NET CLR Version Configuration

For .NET Framework applications (as opposed to .NET Core / .NET 5+ which are self-hosted), you must set the managed runtime version to match the application’s target framework:

# .NET Framework 4.x (covers 4.0, 4.5, 4.6, 4.7, 4.8)
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']" `
  -name "managedRuntimeVersion" -value "v4.0"

# .NET Framework 2.0 / 3.5
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']" `
  -name "managedRuntimeVersion" -value "v2.0"

# No managed code (for native apps, PHP, Node.js proxied via ARR, etc.)
Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']" `
  -name "managedRuntimeVersion" -value ""

Classic vs. Integrated Pipeline Mode

The managed pipeline mode determines how IIS processes requests for .NET applications.

Integrated mode — The ASP.NET request pipeline is fully integrated with the IIS native pipeline. HTTP modules written in .NET participate in all requests, including static files. This is the modern default and the correct choice for all new applications.

Classic mode — Mimics the IIS 6 two-stage pipeline where ASP.NET runs as an ISAPI extension. Required only for legacy applications written specifically for IIS 6 compatibility. Classic mode is slower and less capable than Integrated.

Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
  -filter "system.applicationHost/applicationPools/add[@name='MyApp_Pool']" `
  -name "managedPipelineMode" -value "Integrated"

Application Pool Isolation Best Practices

The most important security practice for IIS is running each application in its own application pool. This ensures that a vulnerability in one application cannot be leveraged to read session tokens, cookies, files, or process memory belonging to another application on the same server.

# Create dedicated pools for each site
$sites = @("SiteA", "SiteB", "SiteC")
foreach ($site in $sites) {
    New-WebAppPool -Name "${site}_Pool"
    Set-WebConfigurationProperty -pspath "MACHINE/WEBROOT/APPHOST" `
      -filter "system.applicationHost/applicationPools/add[@name='${site}_Pool']/processModel" `
      -name "identityType" -value "ApplicationPoolIdentity"
    Set-ItemProperty "IIS:Sites$site" -Name applicationPool -Value "${site}_Pool"
}

Additional isolation recommendations: never share application pools between sites that have different trust levels or belong to different customers; keep privileged management applications (e.g., WordPress admin, phpMyAdmin) in a separate pool and restrict access by IP if possible; and audit application pool identities regularly to ensure no pool has been inadvertently granted elevated privileges.