IIS Application Initialization: Warming Up ASP.NET Apps After Recycles

The first request to an ASP.NET application after an IIS restart is always the slowest. Application pool recycling, a server reboot, or a config change forces the worker process to recompile assemblies and warm caches, and that first user pays the entire cost — often five to thirty seconds of spinner. Application Initialization, built into IIS 8 and later, lets the server do that work before it accepts traffic.

When this matters (and when it does not)

If your application recycles nightly, if you deploy frequently, or if you run more than one node behind a load balancer, warmup turns a user-visible freeze into a background task. For a single long-running app pool that recycles weekly, the payoff is smaller but the setup cost is near zero, so there is little reason to skip it.

Step 1: Install the feature and confirm the module

The role service is called Application Initialization. Install it in an elevated PowerShell session:

Install-WindowsFeature Web-AppInit

# verify it loaded
Get-WebGlobalModule | Where-Object Name -eq 'ApplicationInitializationModule'

If you manage IIS with the Configuration Editor rather than feature management, the module appears under system.webServer/applicationInitialization at the server level.

Step 2: Enable preload on the application and the pool

Two switches must both be set: the application must be marked for preload, and the pool must start automatically. Missing either one silently disables the whole mechanism.

Import-Module WebAdministration

Set-ItemProperty IIS:\Sites\MyApp -Name applicationDefaults.preloadEnabled -Value $true
Set-ItemProperty IIS:\AppPools\MyApp -Name startMode -Value AlwaysRunning
Set-ItemProperty IIS:\AppPools\MyApp -Name processModel.idleTimeout -Value "00:00:00"

Setting idleTimeout to zero stops IIS from unloading the worker process after twenty minutes of inactivity. Combined with AlwaysRunning, the app stays resident and warm between requests.

Step 3: Declare a warmup URL

Preload alone starts the process; it does not exercise the application. Add a web.config entry pointing at a lightweight endpoint that touches the expensive paths without doing real work.

<system.webServer>
  <applicationInitialization doAppInitAfterRestart="true">
    <add initializationPage="/warmup" />
    <add initializationPage="/health/db" />
  </applicationInitialization>
</system.webServer>

IIS will not route traffic to the site until those pages return a successful status. Design /warmup to prime the things that are actually slow: JIT-compile controller types, open the database connection pool, load configuration, and populate any in-memory cache.

Warmup targetWhy it helpsCost
Razor/compiled viewsFirst render is fastLow
DB connection poolFirst query does not pay handshakeLow
In-memory cacheAvoids cold-start stampedeMedium
External API clientsDNS and TLS warmMedium

Step 4: Do not slow down deployment

There is a tradeoff to respect: IIS now waits for your warmup pages before serving traffic, so a slow warmup becomes deployment latency. Keep warmup endpoints fast and idempotent, and cap their runtime at a few seconds. If warmup fails, IIS retries rather than serving a cold site — consider that a feature, but monitor the failed-request log so a broken warmup endpoint does not look like an outage.

Multi-node and load-balanced deployments

Warmup matters most when more than one node serves traffic. Without it, a rolling deployment or a pool recycle on one node sends that node’s share of requests into a cold app while its peers are already warm — the imbalance shows up as a latency spike that looks like a network problem. With preload enabled on every node, the load balancer only needs a health probe that respects the same warmup endpoint.

Deployment styleCold-start exposureWarmup value
Single node, weekly recycleOne windowModerate
Rolling deploy, 2+ nodesPer-node spikeHigh
Autoscaling instancesEvery scale-outVery high
Blue/green swapOn cutoverHigh

Interaction with recycle settings

Application Initialization does not override the recycle schedule — it complements it. Leave a scheduled recycle in place for the memory-reclamation benefit, but pair it with overlapping recycle so a new worker process starts and warms before the old one retires. Check the pool’s Disable Overlapped Recycle setting is off:

Get-ItemProperty IIS:\AppPools\MyApp `
    -Name recycling.disallowOverlappingRotation
# Value should be False; set it explicitly if it is not
Set-ItemProperty IIS:\AppPools\MyApp `
    -Name recycling.disallowOverlappingRotation -Value $false

Overlapped recycle combined with a warmup page means users never touch a cold process, even during a scheduled recycle. This single pairing eliminates the most common “the site is slow every night at 3am” complaint.

Common pitfalls that silently break warmup

  • Warmup page returns a redirect. IIS follows redirects for initialization pages in some configurations; a warmup URL that 302s to the login page may never warm the real code path. Return 200 directly.
  • Preload enabled but pool start mode left on OnDemand. The process still stops, and the next request is cold again.
  • Warmup page depends on an external service that is also cold. Chain the warmup so the DB ping happens before the cache fill, not in parallel.
  • Application Initialization installed but web.config not deployed. The module does nothing without the applicationInitialization section.
  • Health probes configured to ignore warmup. A probe that returns healthy before warmup completes routes traffic to a cold process anyway.

Each of these produces the same symptom — a first request that is still slow — so work through the list in order when warmup appears not to work.

Verify it worked

  1. Recycle the pool: Restart-WebAppPool -Name MyApp
  2. Immediately time a request to a heavy page — it should already be warm.
  3. Check %SystemDrive%\inetpub\logs\LogFiles for the warmup hits before any user request.
  4. Confirm the pool process is running with no request: Get-Process w3wp should show it resident.

Set up correctly, Application Initialization removes the cold-start penalty entirely and makes deployments invisible to users instead of a brief outage. It is one of the highest-value ten-minute IIS changes available, particularly on VPS deployments that lack the spare capacity to absorb a stampede of cold requests.

Leave a Comment