How to Set Up ASP.NET Core with IIS on Windows Server 2025

ASP.NET Core is a cross-platform, high-performance web framework from Microsoft, and Windows Server 2025 with IIS 10.0 is a first-class hosting environment for it. Unlike classic ASP.NET which runs directly inside IIS worker processes, ASP.NET Core applications run as standalone processes that IIS proxies requests to via the ASP.NET Core Module (ANCM). This reverse-proxy architecture gives you the flexibility of hosting your app either in-process (running inside the IIS worker process for maximum throughput) or out-of-process (running in a separate dotnet process with IIS acting as a load-balancing proxy). This guide covers installing the .NET Hosting Bundle, deploying a published ASP.NET Core application, configuring IIS, enabling HTTPS redirection, and reading diagnostic logs.

Prerequisites

  • Windows Server 2025 with IIS 10.0 installed and the server role started
  • A published ASP.NET Core application (created with dotnet publish or Visual Studio)
  • Administrator-level PowerShell session
  • Internet access to download the .NET Hosting Bundle, or the installer pre-staged locally
  • Firewall rules permitting inbound TCP 80 and 443

Step 1: Install the .NET Hosting Bundle

The .NET Hosting Bundle is a single installer that includes three components required to run ASP.NET Core under IIS:

  1. The .NET Runtime (CLR and base libraries)
  2. The ASP.NET Core Runtime
  3. The ASP.NET Core Module (ANCM) — the native IIS module that bridges IIS and the .NET process
# Download the .NET 8 Hosting Bundle
$bundleUrl  = "https://dotnet.microsoft.com/download/dotnet/thank-you/runtime-aspnetcore-8.0-windows-hosting-bundle-installer"
$bundlePath = "C:installdotnet-hosting-win.exe"

New-Item -ItemType Directory -Path "C:install" -Force | Out-Null

# Use the direct installer URL (replace with the current version from https://dotnet.microsoft.com/download)
$directUrl = "https://download.visualstudio.microsoft.com/download/pr/dotnet-hosting-8.0.6-win.exe"
Invoke-WebRequest -Uri $directUrl -OutFile $bundlePath -UseBasicParsing

# Install silently
Start-Process -FilePath $bundlePath `
              -ArgumentList "/install", "/quiet", "/norestart", "OPT_NO_SHAREDFX=0" `
              -Wait

Write-Host "Hosting bundle installed. Restarting IIS..."
iisreset /restart

After the restart, confirm the ANCM module is loaded in IIS:

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

You should see AspNetCoreModuleV2 listed as a native module.

Step 2: Publish the ASP.NET Core Application

On your development machine (or a build server), publish the application to a folder. Use the Release configuration and target the win-x64 runtime identifier:

# Publish a framework-dependent deployment (FDD) — requires .NET installed on the server
dotnet publish MyApp.csproj `
    --configuration Release `
    --runtime win-x64 `
    --self-contained false `
    --output C:publishMyApp

# Or publish a self-contained deployment (SCD) — bundles the .NET runtime
dotnet publish MyApp.csproj `
    --configuration Release `
    --runtime win-x64 `
    --self-contained true `
    --output C:publishMyApp-scd

Transfer the published output folder to the server. A common approach is to place it under C:inetpubappsMyApp:

# On the server — create the deployment target and set permissions
New-Item -ItemType Directory -Path "C:inetpubappsMyApp" -Force | Out-Null

# Copy published files (example using robocopy if transferring from a network share)
robocopy "\buildserverdropsMyApp" "C:inetpubappsMyApp" /E /IS /IT /NFL /NDL

Step 3: Create the IIS Application Pool

ASP.NET Core does not use the .NET CLR inside IIS — the ANCM handles startup. Therefore, the application pool must be configured with No Managed Code as the CLR version. This tells IIS not to load the .NET Framework CLR into the worker process:

Import-Module WebAdministration

$appName  = "MyApp"
$poolName = "MyAppPool"
$sitePath = "C:inetpubappsMyApp"

# Create the application pool with No Managed Code
New-WebAppPool -Name $poolName
Set-ItemProperty "IIS:AppPools$poolName" -Name managedRuntimeVersion -Value ''
Set-ItemProperty "IIS:AppPools$poolName" -Name managedPipelineMode   -Value 'Integrated'

# Configure auto-start and steady-state thresholds
Set-ItemProperty "IIS:AppPools$poolName" -Name startMode             -Value 'AlwaysRunning'
Set-ItemProperty "IIS:AppPools$poolName" -Name processModel.idleTimeout -Value '00:00:00'

Write-Host "App pool '$poolName' created with No Managed Code."

Step 4: Create the IIS Site or Application

You can host the ASP.NET Core app as a root-level website or as a sub-application under an existing site. The example below creates a standalone site:

# Create the website
New-WebSite -Name $appName `
            -PhysicalPath $sitePath `
            -Port 80 `
            -IPAddress "*" `
            -HostHeader "myapp.example.com" `
            -ApplicationPool $poolName `
            -Force

# Add an HTTPS binding (assumes a certificate is already in Cert:LocalMachineMy)
$thumbprint = (Get-ChildItem Cert:LocalMachineMy | Where-Object { $_.Subject -like "*myapp*" }).Thumbprint
New-WebBinding -Name $appName -Protocol "https" -Port 443 -IPAddress "*" -HostHeader "myapp.example.com" -SslFlags 1
$binding = Get-WebBinding -Name $appName -Protocol "https"
$binding.AddSslCertificate($thumbprint, "My")

# Set permissions for the app pool identity
icacls $sitePath /grant "IIS AppPool${poolName}:(OI)(CI)RX" /T

Start-WebSite -Name $appName
Write-Host "Site '$appName' started."

Step 5: Review and Configure web.config

The dotnet publish command generates a web.config in the output folder that configures the ANCM. Review and adjust it for your environment:

<?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=".logsstdout"
        hostingModel="inprocess">

        <environmentVariables>
          <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" />
          <environmentVariable name="ASPNETCORE_URLS"        value="http://localhost:5000" />
        </environmentVariables>
      </aspNetCore>
    </system.webServer>
  </location>
</configuration>

Key attributes to understand:

  • processPath: dotnet for framework-dependent deployments; the executable name (e.g., MyApp.exe) for self-contained deployments.
  • arguments: The DLL to pass to dotnet for FDD; omit for SCD executables.
  • hostingModel: inprocess runs the app inside the IIS worker process (higher performance). outofprocess runs a separate dotnet process and IIS proxies to it.

Step 6: In-Process vs Out-of-Process Hosting

Choose the hosting model based on your requirements:

  • In-process (inprocess): The app runs directly in the IIS worker process (w3wp.exe). There is no loopback proxy overhead, making this the highest-throughput option. The downside is that an application crash can bring down the worker process.
  • Out-of-process (outofprocess): The ANCM starts a separate dotnet.exe or MyApp.exe process. IIS proxies requests to the Kestrel web server running inside that process. Crashes are isolated — IIS continues serving other apps, and the ANCM can restart the failed process automatically.

Switch the model by editing the hostingModel attribute in web.config — no IIS or code changes are required beyond that single attribute.

Step 7: Enable and Read stdout Logs

When the application fails to start, ANCM logs to the Windows Event Log (Application source: IIS AspNetCore Module) and optionally to a stdout log file. Enable stdout logging temporarily to diagnose startup failures:

# Create the logs directory and grant write access
New-Item -ItemType Directory -Path "C:inetpubappsMyApplogs" -Force | Out-Null
icacls "C:inetpubappsMyApplogs" /grant "IIS AppPool${poolName}:(OI)(CI)M"

In web.config, set stdoutLogEnabled="true". Restart the site and reproduce the error, then check the generated log file in the logs folder. Disable stdout logging once the issue is resolved — the logs grow without bound and affect disk performance under load.

# Check Windows Event Log for ANCM errors
Get-EventLog -LogName Application -Source "IIS AspNetCore Module*" -Newest 20 |
    Select-Object TimeGenerated, EntryType, Message | Format-List

Step 8: Configure HTTPS Redirection and Environment Variables

ASP.NET Core’s built-in UseHttpsRedirection() middleware handles HTTP-to-HTTPS redirection at the application layer. Ensure it is registered in your Program.cs:

// Program.cs (ASP.NET Core 8 minimal hosting model)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();

app.Run();

Pass environment-specific settings to the application via <environmentVariables> in web.config rather than hard-coding them. This keeps secrets and environment toggles outside the compiled binary:

<environmentVariables>
  <environmentVariable name="ASPNETCORE_ENVIRONMENT"           value="Production" />
  <environmentVariable name="ConnectionStrings__DefaultConnection"
                       value="Server=dbserver;Database=MyDb;User Id=appuser;Password=secret;" />
  <environmentVariable name="Logging__LogLevel__Default"        value="Warning" />
</environmentVariables>

Conclusion

Your ASP.NET Core application is now running on Windows Server 2025 under IIS with the ASP.NET Core Module handling process management, automatic restarts on crash, and request forwarding. You installed the .NET Hosting Bundle to bring in the runtime and ANCM, configured an application pool with No Managed Code, deployed your published output, and chose an appropriate hosting model. HTTPS redirection and environment variable injection complete the production-ready configuration. Ongoing deployment is straightforward: publish a new build, copy it to the deployment folder (using a rolling swap or staging slot pattern), and recycle the application pool. Combine this setup with Windows Admin Center or a CI/CD pipeline (Azure DevOps, GitHub Actions) to automate deployments and achieve consistent, repeatable releases to your Windows Server 2025 environment.