How to Configure IIS Application Pools on Windows Server 2025

Application Pools are the fundamental isolation mechanism in IIS on Windows Server 2025. Each pool runs as a separate worker process (w3wp.exe) with its own memory space, identity, and lifecycle settings. Correctly configured application pools protect your server from a single misbehaving application crashing the entire web service, allow you to run different applications under different security contexts, and give you fine-grained control over memory consumption and process recycling. This tutorial covers creating and configuring application pools via PowerShell and the WebAdministration module, selecting appropriate identity types, tuning recycling and process model settings, and monitoring pool health in production.

Prerequisites

  • Windows Server 2025 with IIS 10 installed
  • PowerShell 5.1 or later (run as Administrator)
  • The WebAdministration PowerShell module (installed with IIS)
  • A basic understanding of IIS sites and application hosting

Step 1: Create a New Application Pool

The New-WebAppPool cmdlet creates a pool with default settings. Always use a descriptive name tied to the application it serves:

Import-Module WebAdministration

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

# Verify the pool was created
Get-WebConfiguration -Filter "system.applicationHost/applicationPools/add[@name='MyAppPool']"

You can also create a pool and immediately start it:

New-WebAppPool -Name "MyAppPool" -Force
Start-WebAppPool -Name "MyAppPool"
Write-Host "Pool 'MyAppPool' created and started."

Step 2: Set .NET CLR Version

The managed runtime version controls which version of the .NET CLR the worker process loads. For modern ASP.NET Core applications hosted via the ASP.NET Core Module, select No Managed Code. For legacy ASP.NET 4.x applications, use v4.0:

# ASP.NET Core — no CLR (process manages its own runtime)
Set-ItemProperty `
  -Path "IIS:AppPoolsMyAppPool" `
  -Name "managedRuntimeVersion" `
  -Value ""   # Empty string = No Managed Code

# ASP.NET 4.x Classic or Integrated
Set-ItemProperty `
  -Path "IIS:AppPoolsMyAppPool" `
  -Name "managedRuntimeVersion" `
  -Value "v4.0"

# ASP.NET 1.x / 2.x (legacy)
Set-ItemProperty `
  -Path "IIS:AppPoolsMyAppPool" `
  -Name "managedRuntimeVersion" `
  -Value "v2.0"

Step 3: Configure Pool Identity

The pool identity determines the Windows account under which w3wp.exe runs. IIS offers five identity types, each with different privilege levels:

# Built-in identities
# ApplicationPoolIdentity (recommended default — least privilege, unique per pool)
Set-WebConfigurationProperty `
  -PSPath "MACHINE/WEBROOT/APPHOST" `
  -Filter "system.applicationHost/applicationPools/add[@name='MyAppPool']/processModel" `
  -Name "userName" -Value ""
Set-WebConfigurationProperty `
  -PSPath "MACHINE/WEBROOT/APPHOST" `
  -Filter "system.applicationHost/applicationPools/add[@name='MyAppPool']/processModel" `
  -Name "identityType" `
  -Value "ApplicationPoolIdentity"

# NetworkService — shared, limited network access
Set-ItemProperty `
  -Path "IIS:AppPoolsMyAppPool" `
  -Name "processModel.identityType" `
  -Value "NetworkService"

# Custom domain or local user account
Set-ItemProperty `
  -Path "IIS:AppPoolsMyAppPool" `
  -Name "processModel.identityType" `
  -Value "SpecificUser"
Set-ItemProperty `
  -Path "IIS:AppPoolsMyAppPool" `
  -Name "processModel.userName" `
  -Value "DOMAINsvc_webapp"
Set-ItemProperty `
  -Path "IIS:AppPoolsMyAppPool" `
  -Name "processModel.password" `
  -Value "SecureP@ssw0rd!"

When using ApplicationPoolIdentity, the virtual account IIS AppPoolMyAppPool is automatically created. Grant this account NTFS permissions on your application’s content directory:

$acl  = Get-Acl "C:inetpubwwwrootmyapp"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
  "IIS AppPoolMyAppPool", "ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow"
)
$acl.AddAccessRule($rule)
Set-Acl -Path "C:inetpubwwwrootmyapp" -AclObject $acl

Step 4: Configure Recycling Settings

Recycling periodically restarts the worker process to release memory leaks and recover from degraded states. IIS supports time-based, memory-based, and request-count-based recycling triggers:

$poolPath = "IIS:AppPoolsMyAppPool"

# Time-based recycling: restart every 29 hours (offset from midnight to avoid peak hours)
Set-ItemProperty -Path $poolPath `
  -Name "recycling.periodicRestart.time" `
  -Value "29:00:00"

# Memory-based recycling: restart if virtual memory exceeds 1 GB
Set-ItemProperty -Path $poolPath `
  -Name "recycling.periodicRestart.memory" `
  -Value 1073741824   # bytes

# Private memory recycling: restart if private bytes exceed 512 MB
Set-ItemProperty -Path $poolPath `
  -Name "recycling.periodicRestart.privateMemory" `
  -Value 524288   # KB

# Request-count recycling: restart after 100,000 requests
Set-ItemProperty -Path $poolPath `
  -Name "recycling.periodicRestart.requests" `
  -Value 100000

# Log recycling events to the Windows Event Log
Set-ItemProperty -Path $poolPath `
  -Name "recycling.logEventOnRecycle" `
  -Value "Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory"

Overlapped recycling (the default) starts a new worker process before terminating the old one, ensuring zero downtime during recycles.

Step 5: Configure Rapid-Fail Protection

Rapid-fail protection stops a pool that experiences a high rate of worker process failures, preventing a failing application from consuming all server resources in a crash loop:

$poolPath = "IIS:AppPoolsMyAppPool"

# Enable rapid-fail protection
Set-ItemProperty -Path $poolPath `
  -Name "failure.rapidFailProtection" `
  -Value $true

# Maximum failures within the interval before the pool is stopped
Set-ItemProperty -Path $poolPath `
  -Name "failure.maximumFailures" `
  -Value 5

# Time window (minutes) for counting failures
Set-ItemProperty -Path $poolPath `
  -Name "failure.rapidFailProtectionInterval" `
  -Value "00:05:00"

# Action when rapid-fail threshold is reached
Set-ItemProperty -Path $poolPath `
  -Name "failure.loadBalancerCapabilities" `
  -Value "TcpLevel"   # Return TCP RST to ARR/upstream load balancer

Step 6: Configure Process Model Settings

The process model controls how IIS manages worker processes within the pool:

$poolPath = "IIS:AppPoolsMyAppPool"

# Idle timeout: shut down worker process after 20 minutes of no requests
Set-ItemProperty -Path $poolPath `
  -Name "processModel.idleTimeout" `
  -Value "00:20:00"

# Idle timeout action: Suspend (faster resume) or Terminate
Set-ItemProperty -Path $poolPath `
  -Name "processModel.idleTimeoutAction" `
  -Value "Suspend"

# Web Garden: run multiple worker processes for the same pool
# Useful for CPU-bound workloads. Set to number of logical CPU cores.
Set-ItemProperty -Path $poolPath `
  -Name "processModel.maxProcesses" `
  -Value 4

# Startup time limit: how long IIS waits for a worker to start (seconds)
Set-ItemProperty -Path $poolPath `
  -Name "processModel.startupTimeLimit" `
  -Value 90

# Shutdown time limit: how long IIS waits for graceful shutdown (seconds)
Set-ItemProperty -Path $poolPath `
  -Name "processModel.shutdownTimeLimit" `
  -Value 90

Step 7: Enable 32-bit Mode on 64-bit IIS

Some legacy COM components or native DLLs are only available as 32-bit binaries. IIS on Windows Server 2025 runs as a 64-bit host by default but can load 32-bit worker processes for compatible pools:

Set-ItemProperty `
  -Path "IIS:AppPoolsMyAppPool" `
  -Name "enable32BitAppOnWin64" `
  -Value $true

Write-Host "32-bit mode enabled for MyAppPool."

Note that enabling 32-bit mode limits the worker process to approximately 4 GB of addressable virtual memory. Do not use 32-bit mode for modern applications unless a specific native dependency requires it.

Step 8: Monitor Application Pool Health

Use PowerShell to check the state of all application pools and identify any that have stopped or are in an error state:

# List all pools with their current state
Get-WebAppPoolState | Select-Object Name, Value

# Check a specific pool
$state = (Get-WebAppPoolState -Name "MyAppPool").Value
Write-Host "MyAppPool state: $state"

# Restart a stopped pool
if ($state -ne "Started") {
  Start-WebAppPool -Name "MyAppPool"
  Write-Host "MyAppPool restarted."
}

# Query worker process details (PID, CPU, memory)
Get-WmiObject -Class Win32_Process -Filter "Name='w3wp.exe'" |
  Select-Object ProcessId, WorkingSetSize, PageFileUsage,
                @{N='PoolName'; E={ ($_.CommandLine -split '"')[1] }} |
  Format-Table -AutoSize

For continuous monitoring, configure Windows Event Log alerts on Event ID 5002 (application pool disabled due to rapid-fail) and Event ID 5074 (worker process recycle) in the System log.

Step 9: Assign a Site to a Pool

# Create a site using the new pool
New-Website `
  -Name "MyApp" `
  -PhysicalPath "C:inetpubwwwrootmyapp" `
  -ApplicationPool "MyAppPool" `
  -Port 443 `
  -Ssl

# Or move an existing application to a different pool
Set-ItemProperty `
  -Path "IIS:SitesDefault Web Sitemyapp" `
  -Name "applicationPool" `
  -Value "MyAppPool"

Correctly configured IIS Application Pools are the foundation of a stable, secure, and scalable Windows web infrastructure. By isolating each application in its own pool with a least-privilege identity, tuning recycling thresholds to match your application’s actual memory behavior, and enabling rapid-fail protection to prevent runaway crash loops, you significantly reduce the operational risk of running multiple applications on a shared IIS server. Combine these settings with proactive event log monitoring to catch pool failures before they impact end users.