On IIS, ASP.NET Core does not run inside the old System.Web pipeline. The ASP.NET Core Module (ANCM, AspNetCoreModuleV2) owns process lifetime, request handoff, and startup. The single setting that changes latency, crash isolation, and how you debug hangs is hostingModel: inprocess or outofprocess.

For most .NET 10 apps on Windows shared hosts, reseller plans, and single-box Windows VPS installs, in-process is the default you want. Out-of-process is the escape hatch when you need a separate worker process, custom reverse-proxy behavior, or to keep a failing app from taking down the IIS worker. Get the model wrong and you chase the wrong logs, timeouts, and identity problems.

#What ANCM actually does

ANCM is an IIS native module. It reads the aspNetCore element in web.config, starts (or attaches to) your app, and forwards HTTP requests. It also enforces startupTimeLimit, request timeouts in some modes, stdout logging, and the forward Windows auth token when you enable it. App pool settings still matter—pipeline mode must be Integrated, and for in-process the pool’s .NET CLR version must be “No Managed Code.”

  • In-process: app loads inside w3wp.exe (or iisexpress.exe) via a .NET host; ANCM invokes the CoreCLR in the IIS worker.
  • Out-of-process: ANCM starts dotnet.exe (or your exe) as a child; IIS talks to Kestrel over a local loopback port.
  • Both models still use the same publish output and the same AspNetCoreModuleV2 registration on the server.

#In-process: lower hop count, shared worker

With hostingModel="inprocess", requests never leave the IIS worker process. You avoid an extra network hop to Kestrel, which usually cuts p95 latency and simplifies TLS termination (IIS handles HTTPS; the app sees HTTP). The app pool identity is your process identity—no second service account unless you change the pool. Memory and CPU show up under w3wp, which matches how most Windows hosts already monitor sites.

Tradeoffs are real. A native AV fault or hard crash in the app can recycle the whole pool. You cannot run multiple frameworks that fight over the same worker the way old full-framework sites sometimes did. Long-running background work still belongs in a separate Windows service or job, not “because in-process feels permanent.”

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*"
             modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet"
                  arguments=".\MyApp.dll"
                  stdoutLogEnabled="false"
                  stdoutLogFile=".\logs\stdout"
                  hostingModel="inprocess">
        <environmentVariables>
          <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" />
        </environmentVariables>
      </aspNetCore>
    </system.webServer>
  </location>
</configuration>

Keep processPath as "dotnet" for framework-dependent publishes, or point it at your app exe for self-contained. Do not set processPath to an absolute Program Files path on multi-tenant hosts unless every site pins the same runtime layout—prefer the app-local layout your Web Deploy package already contains.

#Out-of-process: isolation and a visible Kestrel

With hostingModel="outofprocess", ANCM launches the app and proxies to Kestrel. The child process can die and restart without always tearing down w3wp the same way. That helps noisy neighbors on a box where one site must not recycle others as aggressively, and it matches mental models from reverse-proxy docs. You also get a clear boundary when attaching debuggers or inspecting dotnet-process dumps separate from IIS.

Cost: another hop, another process to ACL, and two places for timeouts. ANCM’s startupTimeLimit still gates boot, but request behavior depends on both IIS limits and Kestrel. Port exhaustion and rapid-fail protection show up if the app crash-loops. stdout logs remain your first stop when the site returns 502.5.

xml
<aspNetCore processPath="dotnet"
            arguments=".\MyApp.dll"
            stdoutLogEnabled="true"
            stdoutLogFile=".\logs\stdout"
            hostingModel="outofprocess"
            startupTimeLimit="120"
            rapidFailsPerMinute="5">
  <environmentVariables>
    <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" />
  </environmentVariables>
</aspNetCore>

Create the logs folder in the site root and grant the app pool identity Modify on it before you flip stdoutLogEnabled. Turn it off again after you capture the failure—unbounded stdout on a busy site fills disks.

#Verify the module and pool from PowerShell

Before blaming code, confirm the server has AspNetCoreModuleV2 and the pool is not loading a CLR. On Windows Server 2025 with IIS 10.0, these checks catch most “502.5 / module not found” tickets after a fresh image or a partial host install.

powershell
Import-Module WebAdministration

# Module present?
Get-WebGlobalModule | Where-Object { $_.Name -like '*AspNetCore*' }

# Pool must be Integrated + No Managed Code for ANCM apps
Get-ItemProperty IIS:\AppPools\MyAppPool |
  Select-Object name, managedRuntimeVersion, managedPipelineMode, startMode

# Optional: lock the model at the site after deploy
Set-WebConfigurationProperty -PSPath 'IIS:\Sites\MySite' `
  -Filter 'system.webServer/aspNetCore' `
  -Name 'hostingModel' -Value 'inprocess'

#Decision guide that holds up in production

  • Default to inprocess for public ASP.NET Core 10 sites on IIS: fewer moving parts, better latency, one identity to grant SQL and file ACLs.
  • Use outofprocess when you must isolate a unstable app, run a custom server bootstrap, or mirror a non-IIS reverse-proxy topology on the same box.
  • Never mix models casually across slot swaps without testing Web Deploy parameters—web.config is part of the package.
  • Match bitness: a 32-bit pool with a 64-bit self-contained publish fails in either model; prefer 64-bit pools for .NET 10.
  • If you need request-level Windows auth, test in-process first; forwardWindowsAuthToken and your authentication middleware must agree.

Practical takeaway: set hostingModel explicitly in web.config, keep app pools at Integrated / No Managed Code, and treat 502.5 as an ANCM startup problem—enable stdout to the site logs folder, fix the boot error, then disable stdout. Prefer in-process for steady .NET 10 sites on IIS; switch to out-of-process only when isolation or process boundaries are the requirement, not as a generic “performance tweak.” On a Windows host, that one line in web.config is the difference between a clean worker story and a double-process scavenger hunt.