ASP.NET Core App Pool Keeps Recycling: Diagnostic Guide
Every time your IIS Application Pool recycles, your ASP.NET Core app starts over — JIT-compiled hot paths discarded, in-memory caches gone, Blazor Server circuits killed, active SignalR connections dropped. For long-running production apps, an unwanted recycle is the most disruptive routine event the host can throw. The default IIS recycle settings were tuned for classic .NET Framework apps two decades ago and they're often wrong for modern ASP.NET Core. Here's the structured diagnostic guide to why your app pool keeps recycling, ranked by what we actually see in production.
Why app pool recycles hurt ASP.NET Core in particular
A recycle isn't a crash — IIS shuts down the worker process gracefully (with a configurable timeout) and starts a fresh one. For stateless request-response apps that's nearly invisible: the next request triggers a new worker, the JIT recompiles hot paths over the next 30-60 seconds, and steady-state resumes.
For ASP.NET Core specifically, the impact is larger than people expect:
Blazor Server circuits die — every connected user sees the "Attempting to reconnect..." overlay and loses in-memory component state
SignalR connections drop — not just Blazor, any SignalR hub-based feature (live notifications, collaborative editing, dashboards)
JIT warm-up is lost — the first 30-60 seconds of post-recycle traffic is slower as Dynamic PGO re-learns hot paths
In-memory caches reset — IMemoryCache, locally-cached data, session state (if InMemory) all gone
Background work interrupted — IHostedService instances get a graceful-shutdown signal but only have the shutdownTimeLimit window to finish
Connection pool re-initialization — the new worker rebuilds its SQL Server connection pool from cold
For a Blazor Server app or an admin dashboard with WebSocket-based live features, a single unexpected recycle is user-visible — reconnection toasts, lost work, "what just happened?" support tickets.
The 8 causes in order of frequency
- Regular Time Interval (default 29 hours)
IIS's default Regular Time Interval recycle is 1740 minutes — once every 29 hours. The setting exists because classic ASP.NET Framework apps had memory-leak patterns that benefitted from periodic recycles. ASP.NET Core is dramatically less leak-prone, and the default recycle is more disruptive than helpful for modern apps.
Symptom: your app recycles roughly once per day at a non-deterministic time (the 29-hour drift accumulates and the recycle happens at different wall-clock times each day).
Fix: Set Regular Time Interval to 0 (disable) for ASP.NET Core production apps. In IIS Manager → Application Pools → your pool → Advanced Settings → Recycling → Regular Time Interval. In Plesk for Windows, the same setting appears under the application pool's recycling settings.
- Idle Time-out (default 20 minutes)
If no requests reach the app pool for 20 minutes, IIS suspends the worker process to save server resources. The next request after the idle timeout has to wake the worker — cold start, JIT compilation, the whole thing. For low-traffic apps (admin tools, internal portals, staging environments), this creates a "first user every morning waits 5 seconds for the homepage" pattern.
Symptom: the app feels slow for the first request after a quiet period, then fast for everything that follows.
Fix: Set Idle Time-out to 0 (don't suspend on idle). For most production apps this is the right choice — the memory savings from suspending are small, the user experience cost of cold-start is real.
On Adaptive Web Hosting plans, idle timeout is set to 0 by default for all dedicated app pools — the cold-start tax doesn't happen here.
- Private memory limit hit
IIS can recycle the app pool when worker process memory exceeds a configured limit. This is the most common unexpected recycle cause in production — the worker grew gradually, hit the cap, and IIS restarted it.
Common reasons memory grows beyond expected:
In-memory caches without size limits (IMemoryCache with no SizeLimit configured)
Connection leaks in EF Core (see our SQL Server connection timeout playbook)
Long-lived scoped services accumulating state (especially in Blazor Server — see Blazor Server RAM sizing guide)
Static collections (lists, dictionaries) that grow unboundedly with traffic
Slow Gen2 GC under load — the worker can't release memory fast enough
Diagnostic: Check Event Viewer → System logs filtered to source = "WAS". A memory-driven recycle event includes the worker's private bytes at recycle time and the configured limit. Cross-reference with dotnet-counters monitor -n YourApp to identify which heap (Gen0/Gen1/Gen2/LOH) is growing.
Fix: First diagnose the leak. Raising the memory limit just moves the failure point — a recycle once per week is annoying; a recycle once per hour is catastrophic. Tools like dotMemory or PerfView identify the leaking type.
- File change recycling
By default, IIS monitors specific files (web.config, files in /bin, App_Code) and recycles the app pool when they change. This is useful during deployment — your new build's web.config upload triggers a recycle so the new code runs. It becomes a problem when:
Monitoring tools (security scanners, antivirus, file-integrity checkers) touch web.config repeatedly
Logging libraries write log files into the watched folders
Background jobs update configuration files at runtime
Sync tools (Dropbox, OneDrive) erroneously placed inside the site folder
Symptom: random recycles correlating with files being touched, not with traffic or memory.
Diagnostic: Event Viewer WAS events for file-change-triggered recycles include the modified file path. Look for unexpected paths.
Fix: Move logs and runtime-written files out of the watched directories. logs/ outside the site root, or use App_Data/ which IIS doesn't watch by default. For overly-aggressive antivirus, exclude the site root from scanning during operating hours.
- Rapid-Fail Protection triggered
If the worker process crashes 5 times within 5 minutes (defaults), IIS disables the app pool. Until you manually start it or fix the underlying crash, the pool stays down and IIS returns 503 Service Unavailable to every request.
Symptom: app pool shows "Stopped" status, 503 errors site-wide, manual Start → crash within seconds → stopped again.
Diagnostic: The underlying crash is the real problem — the auto-disable is just IIS protecting the server from constant crash loops. Read Event Viewer Application logs filtered to source = ".NET Runtime" for the exception that's killing the worker. See our won't-start article for the full diagnostic flow on startup failures.
Fix: Fix the underlying crash. Rapid-Fail Protection itself shouldn't be disabled — if your app is crashing 5x in 5 minutes, you have a real problem regardless.
- Configuration change recycle
Some changes to web.config or applicationHost.config trigger an automatic recycle. This is mostly desired behaviour — new configuration should take effect — but if you have automation modifying configuration files during operating hours, you get unexpected recycles.
Symptom: recycles correlate with deploy windows, CI/CD pipelines running, or configuration management tools applying state.
Fix: Schedule configuration changes during maintenance windows. Or, if the changes are truly safe to apply mid-day, accept the recycle as cost of doing business.
- Specific Times recycle
IIS supports scheduled recycles at fixed wall-clock times (e.g., "every day at 3 AM"). On Plesk for Windows hosts this is rarely configured by default, but inherited configurations from migrations may have it set.
Symptom: recycles happen at the same exact time every day.
Fix: In IIS Manager → Application Pools → Advanced → Recycling → Specific Times, remove any scheduled times. For an ASP.NET Core production app, scheduled recycles are almost always unhelpful.
- CPU usage limit hit
IIS can throttle or kill the worker process when CPU exceeds a configured percentage for a configured duration. This is rarely the cause of mystery recycles but can fire under sustained heavy load (see our High CPU diagnostic guide for the related symptoms).
Symptom: recycle correlates with sustained 100% CPU spikes on the worker process.
Fix: Diagnose the underlying CPU pattern first; if the spike is legitimate (e.g., a scheduled batch job), schedule it outside business hours or move it to a separate worker.
The diagnostic flow we recommend
Open Event Viewer → Windows Logs → System. Filter by source = "WAS" (Windows Process Activation Service). Every recycle event is logged here with a reason field.
Note the recycle reason in the event. Common reasons: "configured maximum age (private memory) reached," "configured maximum age (regular time interval) reached," "configured number of requests reached," "ISAPI extension marked process unhealthy," "request to recycle pool received."
Cross-reference the recycle timing against your traffic and deploy pattern. Daily-at-different-times = Regular Time Interval (29 hours). Same-time-every-day = Specific Times. After-deploy = Configuration / file change. After load spike = Memory or CPU limit.
Check Application logs for any .NET Runtime exceptions in the same time window — some recycles are triggered by worker crashes (Rapid-Fail Protection).
If memory-related, run dotnet-counters monitor -n YourApp over a longer window to identify which heap is growing. Use dotMemory or PerfView to find the leaking allocation.
If file-change-related, the event message names the modified file. Move logs and runtime files out of watched directories.
Production-ready app pool recycling settings
For an IIS-hosted ASP.NET Core application, these settings minimize unexpected recycles:
SettingDefaultRecommended for ASP.NET Core
Regular Time Interval (minutes)1740 (29h)0 (disabled)
Idle Time-out (minutes)200 (don't suspend)
Private Memory Limit0 (no limit)Set to ~80% of available pool RAM
Virtual Memory Limit0 (no limit)0 (private memory is the more useful limit)
Number of Requests0 (disabled)0 (don't recycle on request count)
Specific Times(empty)(empty — no scheduled recycles)
Rapid-Fail ProtectionEnabled, 5 in 5 minutesEnabled (don't disable — mask underlying crashes)
Maximum Worker Processes11 (web gardens cause more problems than they solve for ASP.NET Core)
Managed Code Versionv4.0No Managed Code (required for ASP.NET Core)
IdentityApplicationPoolIdentityApplicationPoolIdentity (unless you need domain account)
The Private Memory Limit is the one setting where you want a value rather than disabled. Set it ~80% of the pool's available memory — gives you a controlled recycle as a safety net before the worker dies from OOM, while leaving operational headroom.
How Adaptive Web Hosting handles these defaults
On Adaptive Web Hosting's plans, dedicated IIS Application Pools come pre-configured with production-tuned defaults for ASP.NET Core:
Regular Time Interval = 0 — no scheduled recycle
Idle Time-out = 0 — no suspend on idle, no cold-start tax
Private Memory Limit — set proportionally to plan tier (Developer ~1 GB, Business ~2 GB, Professional ~4 GB)
No Managed Code — correct for ASP.NET Core hosting model
Specific Times empty — no clock-driven recycles
Rapid-Fail Protection enabled — if your app crashes 5x in 5 min, the pool disables (you get notified via support ticket)
You can override these via the Plesk for Windows control panel if your specific workload requires different settings — e.g., a Blazor Server app might want a higher private memory limit. The defaults are aimed at "long-running ASP.NET Core production app" rather than "default IIS install from 2010."
When recycles are actually a good idea
Counterpoint: some scenarios benefit from periodic recycles.
Apps with known memory leaks you can't fix — a daily recycle keeps the worker from OOM'ing. Better long-term: fix the leak
Apps that load configuration only at startup and you want to apply config changes without manually recycling. Set a scheduled recycle at 3 AM so daily-published config takes effect overnight
Apps with finite background-work pools — periodic recycle clears stuck background tasks. Better long-term: fix the task-management code so tasks don't get stuck
For most modern ASP.NET Core apps, the answer is "fix the underlying issue so you don't need a recycle as a band-aid."
Recycle vs Restart vs Stop
These three IIS operations are related but distinct:
Recycle — graceful shutdown of the current worker + new worker starts. Active requests in flight finish; new requests go to the new worker. Brief overlap period.
Restart — stop + start. Active requests are dropped. Higher disruption than recycle.
Stop — shutdown without restart. Pool remains stopped until manually started. 503 returned for all requests.
For applying configuration changes, recycle is the preferred operation — minimal user impact. Plesk for Windows exposes both Recycle and Stop/Start buttons; use Recycle in normal operations.
Frequently asked questions
How do I know if my app is recycling unnecessarily?
Look at Event Viewer WAS events over the past week. Count the recycles. For a healthy production ASP.NET Core app with good defaults, you should see 0-2 recycles per week (mostly tied to deploys). If you see daily recycles or worse, something is wrong — cross-reference with the causes list above.
Will reducing recycles improve Blazor Server stability?
Dramatically yes. Every recycle kills every Blazor Server circuit, meaning every connected user sees the reconnection overlay. For a low-recycle app, Blazor Server circuits stay alive for hours or days. For a high-recycle app, the user experience is constant reconnection. If your Blazor Server users complain about losing state, the diagnostic should start with "how often does the app pool recycle?" before assuming SignalR transport issues (though see Blazor Server SignalR drops article for the transport-side causes).
What's the right Private Memory Limit?
Roughly 80% of your app pool's available RAM. The 20% headroom accounts for: short-term allocation spikes during request bursts, garbage collection working memory, framework caches warming up. Setting the limit equal to the pool's RAM means OOM kills happen before the recycle limit fires — you want the recycle to be the soft-limit catch, not the hard fail.
Does Adaptive Web Hosting let me change these settings?
Yes — Plesk for Windows exposes the full IIS app pool configuration. If your workload has specific needs (e.g., a scheduled overnight recycle for a memory-leaky third-party library, a tighter memory limit for stricter cost control), you can override the defaults. The defaults are tuned for typical ASP.NET Core production patterns, not locked.
How is recycling different on .NET 10 LTS vs .NET 8 LTS?
The IIS-level recycle behaviour is identical — the ASP.NET Core Module is the same component handling both runtimes. The .NET 10 runtime has slightly faster startup, so the cost of a recycle (cold-start latency) is marginally lower than on .NET 8. The decision to minimize recycles is the same either way. See our .NET 10 vs .NET 8 LTS performance comparison.
What about web gardens (multiple worker processes)?
Don't use web gardens with ASP.NET Core. The framework's IMemoryCache, IDistributedCache backed by in-memory stores, and singleton services all assume one worker per pool. With multiple workers (Maximum Worker Processes > 1), state diverges between workers and load-balancing decisions become unpredictable. ASP.NET Core's single-worker model is the right pattern for the framework.
Should I disable Rapid-Fail Protection?
No. If your app is crashing 5x in 5 minutes, you have a serious problem — disabling the protection just lets the crash loop continue and serves a stream of 500 errors to users. The auto-disable forces you to actually fix the crash. The Event Viewer Application log has the underlying exception; see our won't-start article for the full crash diagnostic flow.
How can I tell if the recycle is from memory or from time-interval?
The WAS event in Event Viewer includes the recycle reason as a structured field. "Configured maximum age (regular time interval) reached" is time-based. "Configured maximum age (private memory) reached" is memory-based. The two have completely different fixes — verify the reason before changing anything.
Bottom line
App pool recycles are one of the biggest hidden disruptors for production ASP.NET Core apps — particularly Blazor Server and SignalR-heavy workloads where every recycle kills active user state. The default IIS settings were tuned for classic .NET Framework apps from the late 2000s and they're often wrong for modern ASP.NET Core. The two single biggest wins: set Regular Time Interval to 0 and set Idle Time-out to 0. The Private Memory Limit should be set as a safety net, not as a primary recycle trigger.
On Adaptive Web Hosting's ASP.NET Core plans, dedicated IIS Application Pools come pre-configured with production-tuned defaults — no idle timeout, no scheduled recycle, generous memory limits per plan tier, Rapid-Fail Protection enabled, Managed Code set to None. The defaults give you a stable foundation; you can override via Plesk if specific workloads need different settings. Every plan includes a 30-day money-back guarantee. View hosting plans, see our High CPU diagnostic guide for the related memory-pressure picture, or talk to an ASP.NET expert.