How to Host ASP.NET Core 10 on IIS — Windows Server Guide (2026)
Step-by-step guide to hosting ASP.NET Core 10 apps on IIS on a Windows Server VPS. Covers IIS install, .NET 10 Hosting Bundle, app pool config, deployment, and common errors. Tested end-to-end on Raff.
![[HUMAN-REQUIRED: alt text — e.g. 'IIS Manager showing a configured ASP.NET Core site with No Managed Code app pool']](https://raff-images.s3.raffusercloud.com/raff_iis_welcome_page_localhost_2026_04_6a622a5dad.png)
On this page
- In short
- Who this guide is for
- What you'll need
- What changed in .NET 10 / 2026
- Step 1 — Install the Web Server (IIS) role
- Step 2 — Install the .NET 10 Hosting Bundle
- Step 3 — Restart IIS (or skip — see below)
- Step 4 — Publish an ASP.NET Core app
- Step 5 — Create the IIS site and app pool
- Step 6 — Open the firewall for external access
- Common errors
- Hosting multiple ASP.NET Core sites on one Raff Server
- Verify deployment with PowerShell
- Tested on Raff
- What's next
- Sources
Don't have a Windows Server yet?
Deploy Windows Server 2019/2022/2025 in ~2 minutes. 6-month evaluation licence included.
In short
Hosting an ASP.NET Core 10 app on IIS takes 5 steps: install the Web Server (IIS) role, install the .NET 10 Hosting Bundle (Runtime + ASP.NET Core Module V2), publish your app with dotnet publish, create an IIS site with an app pool set to No Managed Code, then grant the app pool identity read access to the published folder. Whole flow runs in under 10 minutes on a fresh Windows Server 2025 install. .NET 10 is the current LTS (supported through November 10, 2028); .NET 9 is STS (ends November 10, 2026, same date as .NET 8 LTS). New deployments should target .NET 10.
Who this guide is for
You want to host an ASP.NET Core web app on Windows Server with IIS as the front-end web server. Common scenarios:
- A small business app (intranet, customer portal, dashboard) that needs to run on a single Windows Server
- An ASP.NET Core API behind IIS (so IIS handles HTTPS, static assets, and request routing while your app handles business logic)
- Multiple ASP.NET Core sites on one server, each with their own IIS bindings and app pools
- Replacing an aging ASP.NET Framework deployment with a modern .NET 10 codebase
If you're hosting at scale (multiple servers, load balancers, container orchestration), Kestrel-only behind a reverse proxy (NGINX, YARP) is usually a better fit than IIS. This guide is for the very common single-server-or-small-cluster case where IIS gives you HTTPS termination, static asset serving, and process management for free.
What you'll need
- A Raff Windows Server 2025 — this guide tested on the Production plan ($35.99 — 4 vCPU / 8 GB RAM / 120 GB NVMe), which is the right size for production small-traffic deployments. Single-site staging works fine on the Small Business plan ($19.99). For 10+ sites or high-traffic production, step up to Heavy Workload ($63.99) or Enterprise ($127.99).
- Local administrator access to the Raff Server via RDP. See our RDP connection guide if you haven't connected before.
- An ASP.NET Core app to deploy. We'll create one as part of this walkthrough using
dotnet new webapp. - Optional but recommended: a separate machine for building (your laptop, a CI runner) — production servers should host apps, not compile them. We cover both single-server and split build/host scenarios.
- Estimated time: 30-45 minutes for first deploy, 5 minutes for subsequent deployments to the same server.
What changed in .NET 10 / 2026
If you're coming from older guides written for .NET 6, 7, 8, or 9, three things are different:
- .NET 10 LTS is the current target. Released November 11, 2025. Supported until November 10, 2028. .NET 9 is STS (Standard Term Support) and ends November 10, 2026 — same date as .NET 8 LTS, because Microsoft extended STS from 18 to 24 months. For new deployments, pick .NET 10.
web.configdefaults to launchingYourApp.exedirectly, notdotnet YourApp.dll. Performance is the same; the syntax is just simpler. If you've copy-pasted olderweb.configtemplates, both formats still work — but the auto-generated one in .NET 10 publishes uses the.exeform.- Hosting Bundle and SDK are now separately installable via winget as
Microsoft.DotNet.HostingBundle.10andMicrosoft.DotNet.SDK.10. No more browsing dotnet.microsoft.com and clicking through GUI installers (unless you prefer to).
Step 1 — Install the Web Server (IIS) role
Open PowerShell as administrator on the Raff Server and run:
powershellInstall-WindowsFeature -Name `
Web-Server, `
Web-Common-Http, `
Web-Static-Content, `
Web-Default-Doc, `
Web-Dir-Browsing, `
Web-Http-Errors, `
Web-App-Dev, `
Web-Net-Ext45, `
Web-AppInit, `
Web-ISAPI-Ext, `
Web-ISAPI-Filter, `
Web-Health, `
Web-Http-Logging, `
Web-Performance, `
Web-Stat-Compression, `
Web-Security, `
Web-Filtering, `
Web-Mgmt-Tools, `
Web-Mgmt-Console `
-IncludeManagementTools
This installs the IIS parent role plus the modules ASP.NET Core needs (Common Http, App Development, ASP.NET 4.8 compatibility, ISAPI filters/extensions for ANCM v2, request filtering, dynamic compression, and the management console).
Verify:
powershellGet-WindowsFeature -Name Web-Server | Format-Table Name, InstallState
Get-Service -Name W3SVC, WAS | Format-Table Name, Status, StartType
You should see Web-Server: Installed, W3SVC: Running, Automatic, WAS: Running, Manual. The Manual start type for WAS (Windows Process Activation Service) is correct — it's started on-demand by W3SVC, not directly. Don't change it.
Confirm IIS is serving requests by browsing http://localhost from Edge on the server:

If you see the multi-language "Internet Information Services" welcome page, IIS is alive.
Article finding from our test: Server 2025 IIS install completes without requiring a reboot. Older Windows Server versions sometimes prompted for one — Server 2025 doesn't. Install-WindowsFeature returned Success: True, RestartNeeded: No, ExitCode: Success on our test VM.
Step 2 — Install the .NET 10 Hosting Bundle
The Hosting Bundle is a single installer that puts down three things you need:
- .NET Runtime (CoreCLR — the engine that runs managed code)
- ASP.NET Core Runtime (Kestrel server, framework libraries)
- ASP.NET Core Module V2 (ANCM v2 — the IIS module that hands HTTP requests off to your app's Kestrel process)
The fastest way is winget:
powershellwinget install --id Microsoft.DotNet.HostingBundle.10 --silent --accept-source-agreements --accept-package-agreements
That downloads ~112 MB and installs in 30-60 seconds. As of April 2026, you'll get version 10.0.7 (the latest patch).
If winget isn't available or you prefer a GUI install, download the bundle from dotnet.microsoft.com/download/dotnet/10.0 — pick the ASP.NET Core Runtime column → Hosting Bundle. The filename is dotnet-hosting-10.0.7-win.exe or similar. Run as administrator.
Refresh PATH in your current PowerShell session
The Hosting Bundle adds C:\Program Files\dotnet\ to the Machine PATH, but your existing PowerShell session won't see it until you reopen the window — or refresh the PATH manually:
powershell$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
This is a common gotcha. If your dotnet command says "not recognized" right after installing, this is why.
Verify the runtime is registered
powershelldotnet --list-runtimes
You should see at minimum:
Microsoft.AspNetCore.App 10.0.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 10.0.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Verify ANCM is registered with IIS
powershellGet-WebGlobalModule -Name "AspNetCoreModuleV2" | Format-Table Name, Image
Expected output:
Name Image
---- -----
AspNetCoreModuleV2 %ProgramFiles%\IIS\Asp.Net Core Module\V2\aspnetcorev2.dll
Article finding from our test: Older Microsoft docs warn that "if IIS was added after the Hosting Bundle, rerun or repair the bundle so ANCM is correctly registered." On Windows Server 2025 with the latest 10.0.7 Hosting Bundle, install order doesn't matter — ANCM registered correctly on our test VM regardless of whether IIS or the bundle went in first. The cautious advice is still valid for older bundles, but the 2026 reality is more forgiving.
Step 3 — Restart IIS (or skip — see below)
Microsoft's docs say "After the Hosting Bundle is installed, a manual IIS restart may be required." On a fresh install with no apps running yet, our test showed this is not strictly necessary — ANCM v2 was already loaded after the bundle install. But it costs you 3 seconds and removes any uncertainty:
powershelliisreset
Output: Internet services successfully stopped followed by Internet services successfully restarted.
If you have running ASP.NET Core apps already on the server, do run iisreset — it ensures all worker processes pick up the latest ANCM. For first-time installs, it's optional.
Step 4 — Publish an ASP.NET Core app
Two paths here. Pick whichever fits your workflow.
Path A — Build on the same server (dev/staging only)
Convenient for a single-server deployment, but not the production-correct pattern (production servers shouldn't have build tools installed). Useful for testing, dev/staging, or small-scale deployments.
You'll need the .NET 10 SDK — separate from the Hosting Bundle:
powershellwinget install --id Microsoft.DotNet.SDK.10 --silent --accept-source-agreements --accept-package-agreements
# Refresh PATH for the current session
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
# Verify SDK is available
dotnet --list-sdks
You should see 10.0.203 [C:\Program Files\dotnet\sdk] (current SDK version as of April 2026).
Article finding from our test: This SDK-vs-runtime distinction is the #1 thing that trips up first-time IIS deployers. The Hosting Bundle has no SDK. The error message when you try dotnet new or dotnet publish without an SDK is helpful (No .NET SDKs were found.) but easy to miss if you assume the bundle gave you everything.
Now create and publish a sample app:
powershell# Make a working folder
New-Item -ItemType Directory -Path "C:\src\RaffSampleApp" -Force
Set-Location "C:\src\RaffSampleApp"
# Create a Razor Pages webapp targeting .NET 10
dotnet new webapp --framework net10.0 --name RaffSampleApp --output .
# Publish to deployment folder (framework-dependent, win-x64)
dotnet publish -c Release -o C:\publish\RaffSampleApp --runtime win-x64 --self-contained false
The first time you run any dotnet command after installing the SDK, you'll see a "Welcome to .NET 10.0!" message with telemetry information. The template will also auto-install an ASP.NET Core HTTPS development certificate — harmless and not used for IIS deployments, but worth knowing it ran.
Path B — Build elsewhere, copy to server (production pattern)
This is what you do for real production deployments. Build on a developer workstation, a CI/CD runner (GitHub Actions, Azure DevOps, GitLab CI), or a dedicated build server, then copy the published output to the Raff Server.
On your build machine:
powershell# From your project directory
dotnet publish -c Release -o C:\publish\YourApp --runtime win-x64 --self-contained false
Then transfer the C:\publish\YourApp folder contents to your Raff Server. Options:
- RDP file transfer — copy from your local machine, paste into RDP session
robocopyover SMB if you have file-share access to the Raff Serverscp/rsyncfrom WSL if you've enabled OpenSSH on the Raff Server- Git-based deploy —
git pullon the server, thendotnet publishlocally (requires SDK on server) - Object storage drop —
aws s3 cpor similar, pull down on the server
Whichever method, the result is the same: your published files sit in a folder on the Raff Server, ready for IIS to point at.
Self-contained vs framework-dependent
We used --self-contained false above. This is framework-dependent publish — your app uses the .NET 10 runtime installed on the server via the Hosting Bundle. Output is small (~5-10 MB for a small app), and runtime updates are independent of your app deployments.
The alternative is --self-contained true:
powershelldotnet publish -c Release -o C:\publish\YourApp --runtime win-x64 --self-contained true
Larger output (~70-100 MB) — bundles the runtime with your app. Useful when:
- The server has no Hosting Bundle (won't help if you also need IIS, since ANCM v2 needs the bundle)
- You want to pin to an exact runtime version per-app
- You're targeting servers you don't fully control
For most Raff Server deployments, stick with framework-dependent + Hosting Bundle. Easier upgrades, smaller deploys, faster builds.
What gets published
After dotnet publish, the output folder contains:
RaffSampleApp.exe— the launcher executable IIS will runRaffSampleApp.dll— the actual app codeRaffSampleApp.deps.json,RaffSampleApp.runtimeconfig.json— runtime configurationappsettings.json,appsettings.Development.json— your config filesweb.config— auto-generated; tells IIS how to launch the app via ANCMwwwroot\— static files (CSS, JS, images)RaffSampleApp.staticwebassets.endpoints.json— static asset routing manifest (.NET 10 specific)
What's in the auto-generated web.config?
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=".\RaffSampleApp.exe" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
</location>
</configuration>
Key things in there:
modules="AspNetCoreModuleV2"— the IIS handler that came with your Hosting BundleprocessPath=".\RaffSampleApp.exe"— IIS launches the exe directly (newer .NET pattern; olderdotnet YourApp.dllform still works)hostingModel="inprocess"— Kestrel runs inside the IIS w3wp.exe worker process. This is the default since .NET 3.0 and gives best performance. The alternativeoutofprocessruns Kestrel as a separate process behind IIS as a reverse proxy (slower; used for compatibility scenarios)stdoutLogEnabled="false"— stdout logs disabled by default (perf reason). We'll show how to enable for debugging in the Common Errors section.<location path="." inheritInChildApplications="false">— prevents this app's config from leaking into nested child apps
You don't need to edit this file for our test. For real production apps, you might enable stdout logging during deploys, or add custom MIME types and headers.
Step 5 — Create the IIS site and app pool
Back on the Raff Server (whether you built locally or copied from elsewhere), create the IIS site:
powershellImport-Module WebAdministration
# Stop and delete the default IIS site (we want our app on port 80)
Stop-Website -Name "Default Web Site" -ErrorAction SilentlyContinue
Remove-Website -Name "Default Web Site" -ErrorAction SilentlyContinue
# Create the application pool
New-WebAppPool -Name "RaffSampleAppPool"
# Set No Managed Code (managedRuntimeVersion = "" is the magic for ASP.NET Core)
Set-ItemProperty -Path "IIS:\AppPools\RaffSampleAppPool" -Name "managedRuntimeVersion" -Value ""
# Create the IIS site pointing at our published folder
New-Website -Name "RaffSampleApp" `
-PhysicalPath "C:\publish\RaffSampleApp" `
-ApplicationPool "RaffSampleAppPool" `
-Port 80
# Grant the app pool identity read access to the published folder
icacls "C:\publish\RaffSampleApp" /grant "IIS AppPool\RaffSampleAppPool:(OI)(CI)RX"
Why "No Managed Code"?
ASP.NET Core doesn't run on the .NET Framework CLR — it runs in its own out-of-process Kestrel server (or inside the IIS w3wp.exe worker process via in-process ANCM). Setting managedRuntimeVersion = "" tells IIS not to load the legacy .NET Framework CLR for this pool. If you forget this, you'll get an HTTP 500.30 error when ANCM tries to launch your app.
Permissions
icacls "..." /grant "IIS AppPool\RaffSampleAppPool:(OI)(CI)RX" grants the app pool's auto-generated virtual identity:
(OI)— Object Inherit (apply to files in the folder)(CI)— Container Inherit (apply to subfolders)RX— Read + Execute (read the files, executeRaffSampleApp.exe)
If your app needs to write files (logs, uploads, generated files), grant (M) Modify access — but only on the specific subfolder where it needs to write, never on the whole site folder. Example for a logs subfolder:
powershellicacls "C:\publish\RaffSampleApp\logs" /grant "IIS AppPool\RaffSampleAppPool:(OI)(CI)M"
Verify
powershellGet-Website -Name "RaffSampleApp" | Format-Table Name, State, PhysicalPath, Bindings
Get-IISAppPool -Name "RaffSampleAppPool" | Format-Table Name, State, ManagedRuntimeVersion
Invoke-WebRequest -Uri http://localhost -UseBasicParsing | Select-Object StatusCode, StatusDescription
Expected:
- Site
State: Started,PhysicalPath: C:\publish\RaffSampleApp - App pool
State: Started,ManagedRuntimeVersion:(blank — that's correct) - HTTP
StatusCode: 200, StatusDescription: OK
Now open Edge on the server and browse to http://localhost:

You should see the RaffSampleApp welcome page with the navbar showing Home and Privacy links, the "Welcome" heading, and footer attribution. This means the full pipeline is working: browser → IIS → ANCM v2 → in-process Kestrel → ASP.NET Core 10 → 200 OK back.
Step 6 — Open the firewall for external access
So far we've only confirmed the app responds locally on the server. To reach it from your browser on another machine:
powershell# Open port 80 (HTTP) in Windows Firewall
New-NetFirewallRule `
-DisplayName "HTTP (TCP 80)" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 80 `
-Action Allow `
-Profile Any
# Open port 443 (HTTPS) too, for when you set up a TLS certificate
New-NetFirewallRule `
-DisplayName "HTTPS (TCP 443)" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 443 `
-Action Allow `
-Profile Any
For a real production deployment, don't leave port 80 open to Any — at minimum, set up a TLS certificate (Let's Encrypt via win-acme works the same way as we covered in our SQL Server hardening guide) and redirect HTTP → HTTPS. For admin endpoints or IIS Manager remote access, scope firewall rules to your admin IP.
Common errors
The five errors you'll most likely encounter, with solutions.
HTTP Error 500.30 — ASP.NET Core app failed to start
The most common ASP.NET Core on IIS error. Causes (in order of frequency):
- App pool has the wrong
managedRuntimeVersion. Should be empty string (""), notv4.0. Re-runSet-ItemProperty -Path "IIS:\AppPools\YourPool" -Name "managedRuntimeVersion" -Value "". - Hosting Bundle missing or wrong version. Confirm
dotnet --list-runtimesshows the ASP.NET Core runtime your app was built against. - App throws on startup. Could be a missing connection string, malformed
appsettings.json, or any unhandled exception inProgram.cs.
To see the actual exception, temporarily enable stdout logging in web.config:
xml<aspNetCore processPath=".\YourApp.exe"
stdoutLogEnabled="true"
stdoutLogFile=".\logs\stdout"
hostingModel="inprocess">
</aspNetCore>
Create the logs folder and grant the app pool write access:
powershellNew-Item -ItemType Directory -Path "C:\publish\YourApp\logs" -Force
icacls "C:\publish\YourApp\logs" /grant "IIS AppPool\YourAppPool:(OI)(CI)M"
Restart IIS (iisreset), reload the page, then read logs\stdout_*.log. The actual exception will be there. Disable stdout logging after diagnosis — it slows the app and the log files grow without rotation.
You can also check the Windows Event Log:
powershellGet-EventLog -LogName Application -Source "IIS AspNetCore Module*" -Newest 10 | Format-List TimeGenerated, Message
HTTP Error 500.19 — Internal Server Error / web.config error
Usually means IIS can't read or interpret web.config. Causes:
- Missing ANCM module. Confirm with
Get-WebGlobalModule -Name "AspNetCoreModuleV2". If empty, repair the Hosting Bundle install. - Corrupt web.config. Validate XML syntax (open in any editor — broken XML is obvious). Re-publish from your build output to regenerate.
- Wrong .NET version targeted.
dotnet --list-runtimesdoesn't include your target framework. Install the right runtime version.
HTTP Error 502.5 — Process Failure
The Kestrel process couldn't start. Same diagnosis as 500.30 — enable stdout logging, check Event Log. Common causes: missing dependency DLL, wrong appsettings.json for the environment, missing connection string.
This error code is associated with older outofprocess hosting model deployments — modern inprocess apps (the default since .NET 3.0) typically show 500.30 instead.
App works on http://localhost from the server but not externally
Almost always Windows Firewall. Run the New-NetFirewallRule commands from Step 6.
If the firewall rules exist but it still doesn't work, check that the Raff Server's network interface itself isn't blocking inbound 80/443. The Raff platform doesn't restrict inbound HTTP/HTTPS by default, but cloud providers in general sometimes do.
Bindings conflict — "Cannot create site because port 80 is in use"
Either the default IIS site is still bound to port 80, or another site already has the same binding. Solutions:
- Stop or remove the conflicting site:
Stop-Website "Default Web Site"thenRemove-Website "Default Web Site" - Use a host header so multiple sites share port 80 by hostname:
Each app needs its own DNS record pointing to the Raff Server.
powershell
New-Website -Name "App1" -PhysicalPath "C:\publish\App1" -ApplicationPool "App1Pool" -Port 80 -HostHeader "app1.example.com" New-Website -Name "App2" -PhysicalPath "C:\publish\App2" -ApplicationPool "App2Pool" -Port 80 -HostHeader "app2.example.com" - Use different ports if you don't have a domain or are testing locally.
Hosting multiple ASP.NET Core sites on one Raff Server
The 4 vCPU / 8 GB Production plan comfortably handles 5-10 small ASP.NET Core sites running side-by-side. Pattern:
- One app pool per site — each site gets its own pool, which means its own w3wp.exe worker process. If one site crashes or hangs, others keep running.
- Each app pool: No Managed Code —
Set-ItemProperty ... -Name "managedRuntimeVersion" -Value ""for every pool. - Different host headers or ports per site — most Raff customers use host headers + Cloudflare for DNS.
- Memory limits per pool — set
recycling.periodicRestart.privateMemoryon each pool so a memory-leaking app can't take down the whole server. Example: limit each pool to 2 GB.
powershell# Limit RaffSampleAppPool to 2 GB private memory before recycle
Set-ItemProperty -Path "IIS:\AppPools\RaffSampleAppPool" `
-Name "recycling.periodicRestart.privateMemory" `
-Value 2097152 # in KB = 2 GB
Production-ready multi-site deployment is a topic deep enough for its own article. The single-site pattern in this guide is the foundation.
Verify deployment with PowerShell
After every deployment, run a quick smoke test to confirm IIS is healthy and your app is responding. Save this as C:\IIS\Verify-Site.ps1:
powershellparam([string]$SiteName = "RaffSampleApp", [string]$Url = "http://localhost")
Write-Host "`n=== IIS Site Health Check: $SiteName ===" -ForegroundColor Cyan
# IIS services
$services = Get-Service -Name W3SVC, WAS
$services | ForEach-Object {
if ($_.Status -eq 'Running') {
Write-Host "PASS: $($_.Name) running" -ForegroundColor Green
} else {
Write-Host "FAIL: $($_.Name) is $($_.Status)" -ForegroundColor Red
}
}
# Site state
$site = Get-Website -Name $SiteName -ErrorAction SilentlyContinue
if (-not $site) {
Write-Host "FAIL: site '$SiteName' not found" -ForegroundColor Red
return
}
if ($site.State -eq 'Started') {
Write-Host "PASS: site '$SiteName' is Started" -ForegroundColor Green
} else {
Write-Host "FAIL: site '$SiteName' is $($site.State)" -ForegroundColor Red
}
# App pool state
$pool = Get-IISAppPool -Name $site.applicationPool -ErrorAction SilentlyContinue
if ($pool.State -eq 'Started') {
Write-Host "PASS: app pool '$($pool.Name)' is Started" -ForegroundColor Green
} else {
Write-Host "FAIL: app pool '$($pool.Name)' is $($pool.State)" -ForegroundColor Red
}
# No Managed Code check
if ($pool.ManagedRuntimeVersion -eq '') {
Write-Host "PASS: app pool managedRuntimeVersion is empty (No Managed Code, correct for ASP.NET Core)" -ForegroundColor Green
} else {
Write-Host "FAIL: app pool managedRuntimeVersion is '$($pool.ManagedRuntimeVersion)' (should be empty for ASP.NET Core)" -ForegroundColor Red
}
# HTTP test
try {
$response = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop
if ($response.StatusCode -eq 200) {
Write-Host "PASS: $Url responds with HTTP 200 OK" -ForegroundColor Green
} else {
Write-Host "WARN: $Url responds with HTTP $($response.StatusCode)" -ForegroundColor Yellow
}
} catch {
Write-Host "FAIL: $Url is not responding — $($_.Exception.Message)" -ForegroundColor Red
}
# Recent error events (last 1 hour)
$events = Get-EventLog -LogName Application -Source "IIS AspNetCore Module*" -After (Get-Date).AddHours(-1) -ErrorAction SilentlyContinue
if ($events) {
Write-Host "WARN: $($events.Count) ANCM event(s) in last hour:" -ForegroundColor Yellow
$events | Select-Object -First 3 | Format-List TimeGenerated, EntryType, Message
} else {
Write-Host "PASS: no recent ANCM errors" -ForegroundColor Green
}
Write-Host "`n=== Health check complete ===" -ForegroundColor Cyan
Run after every deployment:
powershell.\Verify-Site.ps1 -SiteName "RaffSampleApp" -Url "http://localhost"
Every check should show PASS. Any FAIL or WARN is something to investigate before declaring the deployment done.
Tested on Raff
Tested on: ASP.NET Core 10 Razor Pages app on IIS 10 — Microsoft .NET 10 Hosting Bundle 10.0.7, Microsoft .NET SDK 10.0.203 — on a Raff Windows Server 2025 — Production plan, 4 vCPU / 8 GB RAM / 120 GB NVMe, Windows Server 2025 Standard build 26100, Vint Hill, Virginia datacenter — on April 30, 2026, by Serdar Tekin. We verified end-to-end on a fresh VM: IIS install via
Install-WindowsFeaturewith 19 features (no reboot required); .NET 10 Hosting Bundle install via winget (auto-registered AspNetCoreModuleV2 with IIS); .NET 10 SDK install via winget (separate package, required only for build/publish);dotnet new webapp --framework net10.0Razor Pages template; framework-dependent publish toC:\publish\RaffSampleAppwith auto-generatedweb.configusingprocessPath=".\RaffSampleApp.exe"andhostingModel="inprocess"; default site removal + replacement withRaffSampleAppsite on port 80; app poolRaffSampleAppPoolwithmanagedRuntimeVersion=""(No Managed Code); app pool identity granted RX permissions on the published folder viaicacls; HTTP 200 OK confirmed via bothInvoke-WebRequestand Edge browser load of the Razor Pages welcome page. Total time from blank VM to working app on IIS: under 10 minutes of command-execution time. Common-error scenarios documented from Microsoft Learn references rather than live-tested in this session.
What's next
- Install RDS CALs and license multi-user RDP on Windows Server — license dev team RDP access for collaborative deployments
- Install SQL Server 2025 on a Windows Server VPS — the database backend most ASP.NET Core apps need
- SQL Server 2025 Security Hardening on a Windows Server VPS — production-grade SQL hardening, the same Let's Encrypt cert pattern works for IIS HTTPS
- Connect to your Raff Windows Server via RDP — first-step access guide
- Self-Host QuickBooks Desktop Multi-User on Windows Server — for finance + ASP.NET Core combo deployments
Sources
- Microsoft Learn — Host ASP.NET Core on Windows with IIS (canonical)
- Microsoft Learn — The .NET Core Hosting Bundle
- Microsoft Learn — Publish an ASP.NET Core app to IIS
- Microsoft Learn — ASP.NET Core Module (ANCM) for IIS
- Microsoft Learn — Application pools in IIS
- Microsoft Learn — Troubleshoot ASP.NET Core on Azure App Service and IIS
- .NET Blog — Announcing .NET 10
- .NET — .NET and .NET Core official support policy
- .NET — Downloads for .NET 10.0
- Date last verified: 2026-04-30
Microsoft, Windows Server, IIS, .NET, ASP.NET Core, and Edge are trademarks of Microsoft Corporation. Raff Technologies is an independent infrastructure provider and is not affiliated with, sponsored by, or endorsed by Microsoft Corporation.
Related articles
Install SQL Server 2025 on a Windows Server VPS (Standard Developer, 2026 Guide)
End-to-end install guide for SQL Server 2025 Standard Developer on a Windows Server VPS. Covers winget setup, Custom wizard, tempdb, firewall, and post-install CU application.
Self-Host QuickBooks Desktop Multi-User on a Windows Server VPS (2026 Guide)
How to install QuickBooks Desktop in multi-user mode on a Raff Windows Server VPS. Covers QBDSM setup, dynamic ports, firewall, Windows users, RDS CALs, and what Intuit's docs don't emphasize — based on real end-to-end testing on Raff infrastructure.