Introduction to IIS Application Pools on Windows Server 2019

Application Pools in IIS 10 on Windows Server 2019 are isolated worker process containers that host one or more web applications. Each application pool runs as a separate w3wp.exe process, meaning that a crash, memory leak, or security breach in one application cannot directly affect applications running in other pools. Proper application pool configuration — including identity, .NET CLR version, recycling schedule, process model settings, and rapid fail protection — is fundamental to IIS stability, security, and performance.

Understanding Application Pool Architecture

Every IIS website and web application must be assigned to exactly one application pool. The application pool defines the execution environment: whether Classic or Integrated pipeline mode is used, which version of the .NET CLR is loaded (or No Managed Code for PHP/Node.js applications), and which Windows identity the worker process runs under. Integrated pipeline mode is the default and correct choice for all modern ASP.NET applications; Classic mode is only needed for legacy ISAPI applications.

Create an Application Pool

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

# Set .NET CLR version (v4.0 = .NET Framework 4.x, "" = No Managed Code)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" -Name "managedRuntimeVersion" -Value "v4.0"

# Set pipeline mode (Integrated or Classic)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" -Name "managedPipelineMode" -Value 0   # 0=Integrated, 1=Classic

# Enable 32-bit mode (for legacy 32-bit applications on 64-bit OS)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" -Name "enable32BitAppOnWin64" -Value $false

# Start the pool
Start-WebAppPool -Name "MyApp_Pool"

# Verify
Get-WebAppPoolState -Name "MyApp_Pool"

Configure Application Pool Identity

The application pool identity determines what Windows account the worker process runs as. This controls what files, databases, and network resources the web application can access. Windows Server 2019 supports four built-in identity types plus custom service accounts.

ApplicationPoolIdentity (default/recommended): A virtual account automatically created per pool. The account name is IIS AppPoolAppPoolName. Least privilege; recommended for most applications.

NetworkService: A built-in account with network access. Shares identity across all pools using this setting — not recommended.

LocalSystem: Full administrative rights. Never use for web applications.

Custom Account: A specific domain or local user account. Use when the application needs access to specific network resources (file shares, SQL Server with Windows auth).

# Set to ApplicationPoolIdentity (default, recommended)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "processModel.userName" -Value ""
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "processModel.password" -Value ""
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "processModel.identityType" -Value 4   # 4=ApplicationPoolIdentity

# Set to a custom service account
$credential = Get-Credential -UserName "corpsvc-webapp" -Message "Web app service account"
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "processModel.userName" -Value "corpsvc-webapp"
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "processModel.password" -Value ($credential.GetNetworkCredential().Password)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "processModel.identityType" -Value 3   # 3=SpecificUser

Grant ApplicationPoolIdentity Access to File Paths

When using ApplicationPoolIdentity, grant the virtual account access to the web application’s files using the virtual account name:

# Grant the application pool identity read access to the web root
$poolIdentity = "IIS AppPoolMyApp_Pool"
$webRoot = "C:inetpubwwwrootMyApp"
$acl = Get-Acl -Path $webRoot
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
    $poolIdentity, "ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow"
)
$acl.AddAccessRule($rule)
Set-Acl -Path $webRoot -AclObject $acl

# For write access (uploads, logs)
$writeRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
    $poolIdentity, "Modify", "ContainerInherit,ObjectInherit", "None", "Allow"
)
$acl.AddAccessRule($writeRule)
Set-Acl -Path "$webRootuploads" -AclObject $acl

Configure Application Pool Recycling

Recycling periodically restarts the worker process to release memory leaks and reset state. Configure recycling to minimise disruption — use scheduled time recycling rather than random intervals to control when restarts occur:

# Disable random time recycling (the default — occurs at a random time each day)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "recycling.periodicRestart.time" -Value "00:00:00"

# Enable scheduled recycling at 3:00 AM
$recycleTime = New-TimeSpan -Hours 3
$pool = Get-Item "IIS:AppPoolsMyApp_Pool"
$pool.recycling.periodicRestart.schedule.Add($recycleTime)
$pool | Set-Item

# Set memory-based recycling (restart if memory exceeds 1GB)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "recycling.periodicRestart.privateMemory" -Value 1048576  # 1GB in KB

# Set request count recycling (restart after 100,000 requests)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "recycling.periodicRestart.requests" -Value 100000

# Log recycling events
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "recycling.logEventOnRecycle" -Value "Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory"

Configure Process Model — Idle Timeout and Startup Mode

By default, IIS shuts down worker processes that have been idle for 20 minutes (idle timeout). For applications that must be always available, disable the idle timeout or set it to Always Running:

# Disable idle timeout (keep process running indefinitely)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "processModel.idleTimeout" -Value "00:00:00"

# Set startup mode to AlwaysRunning (starts on IIS startup, not first request)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "startMode" -Value 1   # 0=OnDemand, 1=AlwaysRunning

# Set maximum worker processes (for web garden — multiple processes per pool)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "processModel.maxProcesses" -Value 1   # 1 = standard, >1 = web garden

Configure Rapid Fail Protection

Rapid fail protection automatically stops an application pool if it crashes too many times in a short period, preventing a crash loop from consuming server resources:

# Configure rapid fail protection: stop pool if it fails 5 times in 5 minutes
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "failure.rapidFailProtection" -Value $true
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "failure.rapidFailProtectionInterval" -Value "00:05:00"
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "failure.rapidFailProtectionMaxCrashes" -Value 5

# Set action on first failure (Restart worker process)
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "failure.loadBalancerCapabilities" -Value "HttpLevel"

# Configure auto-restart on crash
Set-ItemProperty -Path "IIS:AppPoolsMyApp_Pool" `
    -Name "failure.orphanWorkerProcess" -Value $false

Manage Application Pools in Bulk

# List all application pools with their state and identity
Get-WebConfiguration -Filter "system.applicationHost/applicationPools/add" |
    Select-Object name, state, managedRuntimeVersion, 
                  @{n="Identity";e={$_.processModel.identityType}} | Format-Table

# Stop all pools except the DefaultAppPool
Get-WebAppPoolState | Where-Object { $_.Name -ne "DefaultAppPool" -and $_.Value -eq "Started" } |
    ForEach-Object { Stop-WebAppPool -Name $_.Name }

# Restart all running pools
Get-WebAppPoolState | Where-Object Value -eq "Started" |
    ForEach-Object { Restart-WebAppPool -Name $_.Name }

Summary

Application Pool configuration on IIS Windows Server 2019 is a balance between security isolation, performance, and availability. Use ApplicationPoolIdentity for least-privilege operation, configure scheduled recycling to control maintenance windows, set appropriate memory limits to catch leaky applications before they degrade the server, disable idle timeout for latency-sensitive applications, and enable rapid fail protection to contain crash loops. Assigning each application to its own dedicated pool provides the strongest isolation boundary between hosted applications.