How to deploy a .NET Web API in IIS

Introduction
Not every company is on Azure, and IIS on Windows Server is still the default for a lot of internal .NET APIs. This video walks the full IIS deployment: installing the right components on a clean server, publishing the app, and configuring an application pool the way ASP.NET Core actually needs it configured - not the ASP.NET Framework defaults IIS assumes by default.
🎬 Watch the full video here:
The Hosting Bundle is not optional
A default Windows Server install has IIS, but IIS doesn't know how to run ASP.NET Core out of the box - that requires the .NET Hosting Bundle, which installs the .NET runtime plus the ASP.NET Core Module (ANCM), an IIS module that proxies incoming requests into your app's Kestrel server running behind it:
https://dotnet.microsoft.com/download/dotnet/<version>
→ ASP.NET Core Runtime <version> Hosting Bundle
Skipping this and only installing the plain .NET runtime is the single most common reason a fresh IIS deploy returns a generic 500.19 or 502.5 error with no obvious cause.
The application pool: no Managed Code, please
IIS application pools default to a .NET CLR version and "Managed" pipeline mode - both leftovers from classic ASP.NET, and both wrong for ASP.NET Core, which manages its own runtime entirely outside IIS's CLR hosting:
- .NET CLR Version: No Managed Code
- Managed Pipeline Mode: doesn't matter once CLR version is "No Managed Code," but Integrated is the safe default
ANCM handles process management and forwards requests; IIS's own managed pipeline never touches your app's code at all.
web.config is what wires ANCM to your app
dotnet publish generates a web.config alongside the published output that tells IIS how to start the app:
<aspNetCore processPath="dotnet" arguments=".\MyApi.dll" stdoutLogEnabled="false" />
If this file is missing or malformed after publish, IIS has no idea what to launch - this is worth checking by hand the first time a deploy doesn't come up.
Turn on stdout logging when something's wrong
The fastest way to actually see a startup exception, rather than guessing at IIS's opaque error codes, is flipping stdoutLogEnabled to true temporarily:
<aspNetCore processPath="dotnet" arguments=".\MyApi.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" />
The logs folder needs to exist and be writable by the app pool identity, or this silently does nothing.
App pool identity and folder permissions
The app pool runs under an identity (ApplicationPoolIdentity by default) that needs read access to the published files and write access to the log folder. A deploy that "works" until the first write to disk, then throws an access-denied error, is almost always a permissions gap on that identity rather than a code bug.
Common pitfalls
- Installing the .NET Runtime instead of the Hosting Bundle. The Hosting Bundle is the one that actually adds ANCM.
- Leaving the app pool on "Managed Code." ASP.NET Core needs "No Managed Code" - it's not using IIS's CLR hosting at all.
- A missing or hand-edited-incorrectly
web.config. It's the only thing telling IIS what to launch. - App pool identity lacking write access to the log folder -
stdoutLogEnabledthen does nothing and you're debugging blind. - Forgetting to recycle the app pool after a redeploy and wondering why the old version is still responding.
Common pitfalls
- Not opening the Windows Firewall port the site is bound to, then wondering why it's unreachable from outside the server itself.
Key Takeaways
- Install the .NET Hosting Bundle, not just the runtime - it's what adds the ASP.NET Core Module IIS needs.
- Set the app pool's .NET CLR Version to "No Managed Code"; ASP.NET Core manages its own runtime.
web.config, generated bydotnet publish, is what tells IIS how to launch the app via ANCM.- Enable
stdoutLogEnabled(with a writable log folder) to see the real startup exception instead of a generic IIS error code. - Check app pool identity permissions before assuming a deploy failure is a code bug.
Get the Full Source Code
The complete runnable solution - the API and its generated web.config - is available to Patreon supporters. If you want the exact IIS and app pool settings used instead of rebuilding them from the walkthrough above, you can find it on Patreon.