Control panels

Troubleshooting IIS application pool recycling and crashing

Find out why an IIS application pool keeps recycling or stops completely, read the event log evidence, and apply a fix that sticks.

IntermediateWindows server8 min read

If your IIS application pool keeps recycling or refuses to start, the cause is almost always recorded in the Windows event log before the site goes down. This guide shows you how to read that evidence, tell a scheduled recycle apart from a crash, and fix the underlying fault instead of restarting the pool every hour.

You will work on a Windows VPS with IIS installed, using Event Viewer, the IIS Manager, and a few command-line tools. Nothing here needs a reboot, but one step asks you to stop an application pool, which briefly takes its sites offline.

Before you start

You need:

  • An RDP session to your Windows VPS as an Administrator. Connect to 203.0.113.10:3389 in Remote Desktop. See connect to your Admin RDP if you have not done this yet.
  • IIS with at least one site and application pool. If IIS is not installed, follow install IIS and host a website first.
  • Permission to read the System and Application logs in Event Viewer.
  • A note of the times the pool restarted, or the users who reported errors. Timestamps make the search far faster.

Tip: Open an elevated PowerShell window once and keep it. Most commands below are read-only, so you can gather data without touching the running site.

Take a baseline of the current pool state:

PowerShell
Import-Module WebAdministration
Get-ChildItem IIS:\AppPools | Select-Object Name, State, @{n='AutoStart';e={$_.autoStart}}, @{n='Runtime';e={$_.runtimeVersion}}

Note which pools report Started. Write down the exact name of the pool that misbehaves — you will reuse it in every command.

Read the event log for the real cause

An application pool does not recycle for no reason. Windows logs each event in one of two places: the System log (WAS, the Windows Process Activation Service) and the Application log (W3SVC, ASP.NET,.NET Runtime).

  1. Open Event Viewer (eventvwr.msc).
  2. Go to Windows Logs > System and filter the source to WAS.
  3. Look for these event IDs:
  • 5002 — the application pool has been disabled. This is the important one; it often names the reason.
  • 5010 — a worker process failed to start.
  • 5011 — a process serving an application pool crashed.
  • 5074 / 5076 — a configuration or rapid-fail condition.
  1. Then check Windows Logs > Application for .NET Runtime event 1026 (unhandled exception) and Application Error event 1000 (faulting module).

You can pull the same data from PowerShell:

PowerShell
Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='WAS'; StartTime=(Get-Date).AddDays(-2)} |
  Select-Object TimeCreated, Id, LevelDisplayName, Message |
  Format-List

The message for event 5002 usually contains the phrase rapid fail protection. That means the pool failed more than the allowed number of times within the configured window, and IIS stopped it deliberately. The default is five failures in five minutes. That protection is a good thing — it prevents a crash loop — but it hides the original fault, so keep reading back in time until you find the first failure.

A single recycle with no error entry is different: it is scheduled. Confirm the schedule:

PowerShell
Get-ItemProperty IIS:\AppPools\DefaultAppPool | Select-Object name, recycling
Get-ItemProperty IIS:\AppPools\DefaultAppPool -Name recycling.periodicRestart

If time shows a fixed clock time and you see recycles at exactly that moment, the recycle is policy, not a fault. You can change or disable it later, but it is not the bug.

Identify the failing module or exception

The Application log usually tells you what crashed. Read event 1000 and 1026 carefully.

  • Event 1000 (Application Error): note the faulting application name (w3wp.exe) and the faulting module name. A module name like clr.dll, KERNELBASE.dll or a third-party DLL narrows the search a lot.
  • Event 1026 (.NET Runtime): the exception type and stack trace point at the code or framework call that failed.

Get the last few crashes in one view:

PowerShell
Get-WinEvent -FilterHashtable @{LogName='Application'; Id=1000,1026; StartTime=(Get-Date).AddDays(-3)} |
  Select-Object TimeCreated, Id, ProviderName, Message |
  Format-List

Common patterns and what they mean:

  • Rapid fail with no exception at all — often the application pool identity cannot read its own content folder, or a required environment variable or connection string is missing. Test by running the site under a known-good identity briefly.
  • Stack overflow or 0xC00000FD — deep recursion in application code. No IIS setting fixes this.
  • OutOfMemoryException — the worker process hit its private memory limit. Check recycling.periodicRestart.privateMemory and the actual memory use of w3wp.exe in Task Manager. If the limit is being hit legitimately, the app is leaking memory; raising the limit only delays the crash.
  • Access violation in a third-party ISAPI filter or native module — disable that module on the site and retest.
  • Startup failure right after a deployment — a missing.NET runtime or Hosting Bundle. If the site is ASP.NET Core, see install the.NET Hosting Bundle.

Warning: Removing an ISAPI filter or changing the pool identity takes the site offline while you test. Do it in a maintenance window and tell users first.

For a first quick isolation, enable failed request tracing on the site and reproduce the problem, then read the generated XML log. That shows the exact module and handler where the request died.

Fix the pool configuration

Once you know whether you are chasing a crash or a policy, correct the configuration. These are the settings that cause the most false alarms.

Check idle timeout first. A pool that stops after a quiet period looks like a crash to users:

PowerShell
Get-ItemProperty IIS:\AppPools\DefaultAppPool | Select-Object name, processModel
Set-ItemProperty IIS:\AppPools\DefaultAppPool -Name processModel.idleTimeout -Value "00:00:00"

A idleTimeout of 00:00:00 disables idle shutdown. This is often all that is needed on a VPS with light overnight traffic.

Next, review the recycle schedule and memory limits. Setting privateMemory to 0 removes the memory-based recycle:

PowerShell
Set-ItemProperty IIS:\AppPools\DefaultAppPool -Name recycling.periodicRestart.privateMemory -Value 0
Set-ItemProperty IIS:\AppPools\DefaultAppPool -Name recycling.disallowOverlappingRotation -Value $true

disallowOverlappingRotation stops a new worker process starting before the old one exits. It costs a few seconds of downtime but prevents two processes fighting over the same log file or port.

Then confirm the identity and its permissions. The default ApplicationPoolIdentity is a virtual account; it needs read access to the site folder and, for some apps, write access to a logs or temp directory.

PowerShell
icacls "C:\inetpub\wwwroot\mysite" /grant "IIS AppPool\DefaultAppPool:(OI)(CI)(RX)"

If you run the pool under a custom account, verify the password has not expired. An expired service account password causes exactly the rapid-fail pattern with a vague log entry.

Finally, warm up the pool after any change so you know it starts cleanly:

PowerShell
Restart-WebAppPool -Name "DefaultAppPool"
Start-Sleep -Seconds 5
(Get-WebAppPoolState -Name "DefaultAppPool").Value

Expect Started. If the state flips back to Stopped within seconds, the fault is still present — go back to the event log rather than restarting again.

Check it works

Watch the pool for a realistic period, at least an hour of normal traffic, and confirm three things.

  1. The pool stays Started:
PowerShell
1..10 | ForEach-Object { (Get-WebAppPoolState -Name "DefaultAppPool").Value; Start-Sleep -Seconds 30 }
  1. The worker process ID stays the same, meaning no hidden recycles:
PowerShell
Get-Process w3wp -ErrorAction SilentlyContinue | Select-Object Id, ProcessName, StartTime, WorkingSet64

A StartTime that keeps advancing means recycles are still happening.

  1. No new WAS 5002 or 5011 entries appear after your change:
PowerShell
Get-WinEvent -FilterHashtable @{LogName='System'; ProviderName='WAS'; StartTime=(Get-Date).AddHours(-1)} |
  Select-Object TimeCreated, Id, Message

Then load the site in a browser through your normal URL and confirm pages render and logins work. A pool that stays up but serves 500 errors is a different problem, usually permissions or a bad connection string.

Troubleshooting

  • Pool shows Stopped and will not start → the identity password is wrong or the account is locked out. Reset the password in the pool's advanced settings, or switch to ApplicationPoolIdentity to test.
  • Event 5002 returns minutes after you start the pool → rapid fail protection is still counting old failures. Fix the root cause first; the counter clears once the pool runs cleanly for the configured interval.
  • Site works, then fails every few hours with no error → a scheduled recycle or idle timeout. Check recycling.periodicRestart.time and processModel.idleTimeout before blaming the application.
  • w3wp.exe memory climbs until the pool recycles → the app is leaking. Confirm with a memory counter over an hour, then fix the code or module. Raising the limit only lengthens the interval.
  • Errors started after a Windows update or.NET upgrade → the runtime version may have changed. Check runtimeVersion on the pool and reinstall the matching Hosting Bundle.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.