All articles

Host an ASP.NET Core Web API in IIS

Publish and host ASP.NET Core in IIS: the ASP.NET Core Module, Hosting Bundle, app pool settings that matter, and the 500.19/500.30/502.5 troubleshooting table.

0 · log in to like, save & follow Share on LinkedIn Share on X
Host an ASP.NET Core Web API in IIS

Plenty of .NET teams ship to Windows Server, and there IIS remains the front door. Hosting ASP.NET Core in IIS is different from the old Framework days — your app runs as its own process behind the ASP.NET Core Module, not inside w3wp — and understanding that model turns the classic 500.19/500.30 troubleshooting sessions into five-minute fixes. This guide covers publish, site setup, the app pool settings that matter, and the errors everyone meets.

Host an ASP.NET Core Web API in IIS

How hosting actually works

ASP.NET Core apps are self-contained Kestrel web servers. IIS sits in front via the ASP.NET Core Module (ANCM) in one of two modes:

  • In-process (default): your app loads inside the IIS worker process. Fastest — no proxy hop.
  • Out-of-process: IIS reverse-proxies to Kestrel running as a separate process. Slightly slower; needed only for edge cases (e.g. multiple apps sharing one process model constraint).

Stay with in-process unless you have a reason. Either way, web.config is generated at publish time to tell ANCM how to launch you — you rarely edit it by hand.

Prerequisites on the server

  1. Install IIS (Server Manager → Web Server role, or Enable-WindowsOptionalFeature -Online -FeatureName IIS-WebServer).
  2. Install the .NET Hosting Bundle for your runtime version (search "dotnet hosting bundle" on Microsoft's download page) — it contains ANCM plus the runtime. Install it after IIS, or repair it, or ANCM won't register.
  3. iisreset after installing.

The Hosting Bundle version must be ≥ your app's target framework. A 500.31 ("Failed to load ASP.NET Core runtime") almost always means the bundle is older than the app.

Publish the app

From your project (or CI, per the CI/CD article):

dotnet publish -c Release -o ./publish

Framework-dependent publish (the default) keeps output small and patches the runtime server-side via the Hosting Bundle; --self-contained true -r win-x64 bundles the runtime per-app instead — bigger, but immune to server runtime versions. Visual Studio's right-click Publish with a Folder or Web Deploy profile produces the same output; Web Deploy (msdeploy) can push straight to IIS including app-offline handling.

The publish folder contains your DLLs plus the generated web.config:

<aspNetCore processPath="dotnet" arguments=".\MyApi.dll" stdoutLogEnabled="false"
            stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />

Create the site

In IIS Manager:

  1. Application Pools → Add — name it after the app, and set .NET CLR Version = "No Managed Code". ASP.NET Core doesn't use the CLR loaded by IIS; leaving v4.0 selected is harmless noise, but No Managed Code states the truth.
  2. Sites → Add Website — point the physical path at the publish folder, bind host name/port, pick the new pool.
  3. Grant the pool identity read access to the folder: the identity is IIS AppPool\<PoolName> (add it in the folder's Security tab).

App pool settings worth changing from defaults: Start Mode = AlwaysRunning and Idle Time-out = 0 for APIs (the default 20-minute idle recycle gives your first user after lunch a cold start), and consider disabling the periodic 29-hour recycle in favor of scheduled recycling at low-traffic hours.

The troubleshooting table

Error Meaning Fix
500.19 web.config can't be read Hosting Bundle missing (ANCM not registered) or folder ACLs
500.30 app failed to start Run dotnet MyApi.dll in the folder — the console shows the real exception
500.31 can't load runtime Hosting Bundle older than app's target framework
502.5 (out-of-process) Kestrel process died Same as 500.30 — start it manually and read the error
403.14 IIS wants a directory listing Site's physical path points at the wrong folder (not the publish output)

The universal move is the second row: run the app directly on the server (dotnet MyApi.dll from the publish folder). IIS hides startup exceptions; the console prints them. For persistent capture, flip stdoutLogEnabled="true" temporarily — and turn it back off, because stdout logs grow without rotation. Real logging belongs to Serilog with rolling files.

Environment, HTTPS, and deployment hygiene

  • Set ASPNETCORE_ENVIRONMENT=Production (and secrets like connection strings) via Configuration Editor → system.webServer/aspNetCore → environmentVariables, or machine-level environment variables — not by editing appsettings.json on the server.
  • Bind the HTTPS certificate in IIS (Bindings → https → select cert); IIS terminates TLS and ANCM forwards the scheme so Request.IsHttps behaves.
  • Deploying over a running site fails on locked DLLs: drop an app_offline.htm into the folder (IIS gracefully stops the app, serves that page, and releases locks), copy files, delete it. Web Deploy and most CI tasks automate exactly this.

When IIS is still the right answer

On Windows infrastructure you already operate, IIS gives you process supervision, TLS management, and ops familiarity for free. Outside it, the same publish output runs behind Nginx/Caddy on Linux, or in a container — geeksarray.com itself ships that way. The app doesn't change; only the front door does — which is precisely what the Kestrel-behind-a-proxy model was designed for.

Enjoyed this article? Get the best GeeksArray articles in your inbox — once a week, no spam, unsubscribe anytime.

Comments (0)

Log in to join the conversation.

No comments yet — be the first to share your thoughts.