ASP.NET Core apps run on IIS through the ASP.NET Core Module (ANCM), which either hosts the app in-process inside the IIS worker (fastest), or forwards requests to the app’s own Kestrel server (out-of-process). Setting it up takes a few steps. Most deployment errors come from a missing runtime, permissions, or configuration.
1. Install IIS and the Hosting Bundle
Install-WindowsFeature Web-Server -IncludeManagementTools
Then download and install the .NET Hosting Bundle for your app’s .NET version (for example .NET 8 or 9) from Microsoft’s .NET download page. It installs the .NET runtime, the ASP.NET Core runtime, and the ASP.NET Core Module. Restart IIS afterwards:
iisreset
If you installed the bundle before IIS, run the bundle’s installer again with “Repair”. See install IIS.
2. Publish the app
On your build machine (or in CI):
dotnet publish -c Release -o ./publish
- Framework-dependent (default): uses the runtime from the Hosting Bundle. That makes a smaller deployment.
- Self-contained (
--self-contained -r win-x64): includes the runtime, for when you can’t install runtimes on the server.
Copy the publish folder to the server, for example C:\Sites\myapp. The publish output includes a generated web.config.
3. Create the app pool and site
Import-Module WebAdministration
New-WebAppPool -Name "myapp"
Set-ItemProperty IIS:\AppPools\myapp -Name managedRuntimeVersion -Value "" # "No Managed Code"
New-Website -Name "myapp" -PhysicalPath "C:\Sites\myapp" -ApplicationPool "myapp" -HostHeader "app.example.com" -Port 80
- The app pool must use No Managed Code: ASP.NET Core doesn’t use the classic .NET CLR pipeline.
- Give the app pool identity read access to the folder, and write access only where needed (logs, uploads):
icacls "C:\Sites\myapp" /grant "IIS AppPool\myapp:(OI)(CI)RX"
icacls "C:\Sites\myapp\logs" /grant "IIS AppPool\myapp:(OI)(CI)M"
4. The web.config
The generated file looks like this:
<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>
hostingModel="inprocess"(the default) gives the best performance.- Put secrets (connection strings, API keys) in environment variables or a secure store, not in source-controlled
appsettings.json. See secrets management.
5. HTTPS
Get a free certificate with win-acme, and redirect HTTP to HTTPS. See IIS SSL certificates. If the app uses UseHttpsRedirection() and UseForwardedHeaders(), make sure they work correctly behind IIS. With in-process hosting, IIS passes the scheme correctly.
Troubleshooting
| Error | Usual cause |
|---|---|
| HTTP 500.19 | ASP.NET Core Module not installed (Hosting Bundle missing, or installed before IIS) |
| HTTP 500.30 – In-process start failure | App crashed at startup: bad config, missing connection string, exception in Program.cs |
| HTTP 500.31 / 500.32 | Wrong .NET runtime version installed, or bitness mismatch |
| HTTP 502.5 (out-of-process) | The app process failed to start |
| 403 / 404 on static files | Folder permissions, or wwwroot not published |
To see startup errors, temporarily set stdoutLogEnabled="true" (create the logs folder), reproduce the error, read the log, then switch it off. Also check Event Viewer → Windows Logs → Application for “IIS AspNetCore Module” events. See Windows Event Viewer errors.
Deploying updates without file locks
Running apps lock their DLLs. To deploy:
- Drop an
app_offline.htmfile into the site folder. ANCM shuts the app down, and serves that page. - Copy the new files.
- Delete
app_offline.htm.
Web Deploy (msdeploy), or CI pipelines (GitHub Actions with a self-hosted runner), can automate this.
Quick reference
| Step | Action |
|---|---|
| Runtime | .NET Hosting Bundle, then iisreset |
| Build | dotnet publish -c Release |
| App pool | No Managed Code |
| Hosting model | inprocess (default) |
| Startup errors | stdout log + Event Viewer |
| Updates | app_offline.htm |
Related: IIS URL Rewrite and ARR and Windows VPS.