Out of the box, many IIS app pools still recycle on a rolling ~29-hour timer. That clock starts when the pool is created or last recycled, so the next bounce often lands in the middle of a business day. Long-running ASP.NET requests, warm caches, compiled views, and EF Core model snapshots all pay the cost again—and any request still draining when the old worker is killed can fail hard.

You do not need Kubernetes-style rolling deploys to fix this on Windows shared hosting or a Windows Server box. You need a deliberate recycle schedule, overlapped recycling left on, realistic shutdown and startup time limits, and memory caps that catch leaks without thrashing healthy processes. Those four knobs cover most hosting-floor pain.

#Kill the random 1740-minute default

Periodic restart by elapsed time is fine for leaking legacy apps; it is a poor default for a stable .NET 10 site. Prefer a fixed daily (or twice-daily) schedule in your quiet window, and clear the rolling time interval so IIS does not also bounce on the old clock.

On a server you administer, PowerShell against the WebAdministration module is the clearest way to set this. Replace the pool name and pick a local-server time when traffic is lowest:

powershell
Import-Module WebAdministration
$pool = "MyAspNetAppPool"

# Disable rolling time-based recycle (TimeSpan zero)
Set-ItemProperty "IIS:\AppPools\$pool" `
  -Name recycling.periodicRestart.time -Value "00:00:00"

# Quiet-hour recycle (server local time)
Clear-ItemProperty "IIS:\AppPools\$pool" `
  -Name recycling.periodicRestart.schedule
New-ItemProperty "IIS:\AppPools\$pool" `
  -Name recycling.periodicRestart.schedule `
  -Value @{value="03:30:00"}

# Optional: also recycle on config change only when you intend it
Set-ItemProperty "IIS:\AppPools\$pool" `
  -Name recycling.disallowRotationOnConfigChange -Value $false

On multi-tenant shared hosts you often cannot edit applicationHost.config yourself. In that case ask support for a scheduled recycle window, or design the app so a mid-day recycle is cheap: short request timeouts, no sticky in-process state, and Data Protection keys stored off the worker (file share, Redis, or blob) so cookies survive the bounce.

#Keep overlapped recycle and raise shutdown limits

Overlapped recycling is the IIS feature that starts a new w3wp.exe before the old one exits. Leave it enabled unless you have a hard dependency that cannot run two workers at once (rare exclusive file locks, single-instance background services stuffed into the web process). Pair it with a shutdown time limit long enough for your slowest legitimate request to finish.

powershell
$pool = "MyAspNetAppPool"

Set-ItemProperty "IIS:\AppPools\$pool" `
  -Name recycling.disallowOverlappingRotation -Value $false

# Default shutdown is often 90s — raise if you have long exports/reports
Set-ItemProperty "IIS:\AppPools\$pool" `
  -Name processModel.shutdownTimeLimit -Value "00:03:00"

Set-ItemProperty "IIS:\AppPools\$pool" `
  -Name processModel.startupTimeLimit -Value "00:02:00"

# Idle timeout: 0 = never idle-out (use with care on shared memory)
Set-ItemProperty "IIS:\AppPools\$pool" `
  -Name processModel.idleTimeout -Value "00:00:00"

For ASP.NET Core on the ASP.NET Core Module, the host receives a stop signal and should honor ApplicationStopping. Keep request bodies and background work cooperative so the old worker can exit inside shutdownTimeLimit instead of being terminated.

csharp
// Program.cs — fail fast on stop instead of starting heavy work
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapPost("/reports/run", async (HttpContext ctx, IHostApplicationLifetime life) =>
{
    life.ApplicationStopping.ThrowIfCancellationRequested();
    using var linked = CancellationTokenSource.CreateLinkedTokenSource(
        ctx.RequestAborted, life.ApplicationStopping);

    await RunReportAsync(linked.Token);
    return Results.Ok();
});

app.Run();

#Memory recycle: safety net, not a heartbeat

Private memory limits are useful when a pool slowly leaks or spikes after a bad deploy. They are a poor substitute for fixing the leak. Set a ceiling above normal steady-state working set with headroom for GC and spikes; recycling every hour because the limit is too tight just produces constant cold starts.

  • Measure steady private bytes under real traffic for a few days before picking a limit.
  • Prefer private memory limit over virtual memory limit for modern .NET; virtual figures mislead on 64-bit workers.
  • After a memory-triggered recycle, check Event Log and your stdout logs—treat it as an incident, not routine hygiene.
  • On 64-bit pools (the right default for .NET 10), avoid tiny limits copied from old 32-bit guidance.

#Rapid-fail and what shared plans usually lock

Rapid-fail protection disables the pool after repeated startup crashes. Leave it on; it stops a death loop from burning CPU. If you see the pool disabled after a bad publish, fix the startup exception (missing runtime, bad connection string, locked assembly) before raising failure thresholds.

Shared Windows hosting often fixes identity, enables Full Trust for legacy ASP.NET, and restricts who can rewrite pool recycling. You still control app behavior: avoid in-process singletons that must run exactly once, use durable Data Protection keys, keep SQL connection strings in a transform or environment-specific config the host allows, and make shutdown cooperative. On a Windows VPS or dedicated Server 2025 box you own the pool—schedule the recycle, keep overlap, and document the quiet hour next to your deploy runbook.

Practical takeaway: set one predictable recycle in the lowest-traffic hour, disable the rolling 29-hour timer, keep overlapped rotation on, give shutdown at least as long as your slowest real request, and treat memory-based recycles as alerts. That combination keeps IIS app pools boring—which is exactly what you want under live ASP.NET traffic.