A lightweight, high-performance ASP.NET Core (.NET 8) Web API sidecar application designed for Windows Server & IIS 10 environments. It continuously monitors underlying host OS health and delivers real-time health status probes (/api/health) for load balancers such as F5 BIG-IP.
- Solution Overview
- Prerequisites (What to Install First)
- Repository Architecture & Code Structure
- Build & Publish
- Step 1: Configuration (
appsettings.json) - Step 2: Configure the Sidecar Site in IIS 10
- Step 3: Test and Verify Locally
- Step 4: Secure the Endpoint for F5 BIG-IP Only
- Monitor with BIG-IP
- Troubleshooting Guide
The IIS 10 Health Check Sidecar is a dedicated management microservice hosted alongside your production IIS web applications (typically bound to an isolated management port such as 8080). It exposes clean HTTP status endpoints (200 OK vs. 503 Service Unavailable) and structured JSON health metrics designed specifically for F5 BIG-IP LTM health monitors.
Traditional load balancer health monitors probe only the web application's root URL (e.g., GET /). While this confirms that IIS can serve a basic static page, it creates major operational blind spots in production:
- The "CPU Exhaustion" Blind Spot: A web server running at 100% CPU will continue responding to simple HTTP pings, but will stall or time out when real users attempt complex application requests.
- The "Thread Deadlock" Blind Spot: When backend database connection pools exhaust or worker threads deadlock, CPU usage might remain deceptively low (15–20%), while incoming requests pile up in
http.sys. Standard HTTP monitors fail to detect this hanging queue. - The "Disk Crash" Blind Spot: Out-of-disk conditions on
C:\cause IIS logging to stall, temporary ASP.NET Core compilations to fail, and AppPool worker processes to crash unexpectedly.
The Solution: By deploying this health check sidecar, F5 BIG-IP evaluates real-time system resource health before routing client traffic to a node. If any host resource crosses its safe operating threshold, the sidecar returns HTTP 503, allowing BIG-IP to gracefully drain traffic from the saturated node before end-users experience errors or outages.
- Background Resource Sampler: An ASP.NET Core
BackgroundService(MetricsCollectorService) samples Windows OS metrics every 1 second in a non-blocking background thread using native Windows Win32 P/Invoke APIs (kernel32.dll),DriveInfo, andPerformanceCounter. - Configurable Thresholds & Toggles: Administrators can selectively enable or disable individual health checks and tune threshold limits in
appsettings.json. - Automated Health Evaluation: When F5 BIG-IP sends an HTTP probe (
GET /api/health), the endpoint compares current samples against configured thresholds:- All Enabled Checks Pass: Returns
HTTP 200 OKwith JSON metrics. - Any Threshold Breached: Returns
HTTP 503 Service Unavailablewith a detailed failure reason.
- All Enabled Checks Pass: Returns
Administrators can independently enable, disable, and configure thresholds for the following key metrics in appsettings.json:
- 🖥️ CPU Usage (
MaxCpuPercentage):- How it works: Uses Windows native
GetSystemTimesWin32 kernel API to calculate total host CPU utilization across all cores. - Default Threshold:
85.0%max CPU.
- How it works: Uses Windows native
- 🧠 System Memory / RAM (
MaxMemoryPercentage):- How it works: Uses Windows native
GlobalMemoryStatusExWin32 API to measure physical RAM consumption across the Windows host. - Default Threshold:
90.0%max RAM.
- How it works: Uses Windows native
- 💾 Free Disk Space (
MinDiskSpacePercentage):- How it works: Uses .NET
DriveInfoto measure remaining free disk space percentage on the system drive (C:\). - Default Threshold:
10.0%min free space remaining.
- How it works: Uses .NET
- 🚦 IIS Request Queue Length (
MaxQueueLength):- How it works: Queries the Windows Performance Counter (
HTTP Service Request Queues\CurrentQueueSize) for your application's IIS AppPool to measure requests queued inhttp.sys. - Default Threshold:
50max queued requests.
- How it works: Queries the Windows Performance Counter (
Before starting, ensure your Windows Server has the required IIS Role Features and .NET runtime installed.
Ensure the following IIS features are installed via Server Manager or PowerShell:
| Feature Category | IIS Feature Name (WindowsFeature) |
Status | Purpose |
|---|---|---|---|
| Web Server Core | Web-Server, Web-WebServer |
Required | Core IIS 10 HTTP hosting engine. |
| App Development | Web-ISAPI-Ext, Web-ISAPI-Filter |
Required | Native module handlers required by AspNetCoreModuleV2. |
| Web Security | Web-IP-Security (IP and Domain Restrictions) |
Recommended | Restricts sidecar port access (default: 8080) strictly to F5 BIG-IP Self-IP addresses. |
| Web Security | Web-Filtering (Request Filtering) |
Recommended | Restricts unneeded HTTP verbs and dangerous request payloads. |
| Diagnostics | Web-Http-Logging, Web-Http-Errors |
Recommended | Standard HTTP error handling and request logging. |
| Management | Web-Mgmt-Console (IIS Management Console) |
Recommended | GUI management console (inetmgr). |
To install all required and recommended IIS features at once, open PowerShell as Administrator and run:
Install-WindowsFeature -Name Web-Server, Web-WebServer, Web-Common-Http, Web-Default-Doc, Web-Http-Errors, Web-Static-Content, Web-Health, Web-Http-Logging, Web-Performance, Web-Stat-Compress, Web-Security, Web-Filtering, Web-IP-Security, Web-Mgmt-Console -IncludeManagementTools- Download the .NET 8.0 Hosting Bundle Installer.
- Run
dotnet-hosting-8.0.x-win.exeon the Windows Server.
Important
You must install the Hosting Bundle, not just the .NET SDK or .NET Runtime alone. The Hosting Bundle registers the native AspNetCoreModuleV2 into the IIS schema.
Open Command Prompt (CMD) as Administrator and execute:
net stop was /y
net start w3svc(or run iisreset)
├── HealthCheckSidecar.csproj # .NET 8 Web API Project File
├── Program.cs # Application entrypoint & health routes (/api/health)
├── Models/
│ └── HealthOptions.cs # Strong-typed threshold settings
├── Services/
│ └── MetricsCollectorService.cs # Background service sampling live CPU & Memory
├── appsettings.json # Resource threshold configuration
├── web.config # IIS AspNetCoreModuleV2 handler configuration
├── health.asp # Classic ASP legacy reference script
└── readme.md # Documentation
To compile and publish the sidecar application for deployment:
- Clone or download this repository.
- Open a Command Prompt or Terminal in the repository folder.
- Run the publish command:
dotnet publish -c Release -o C:\inetpub\HealthCheckSidecar
This compiles HealthCheckSidecar.dll and outputs all required runtime assets to C:\inetpub\HealthCheckSidecar.
Edit appsettings.json in your deployment directory to select which metrics to monitor and customize resource thresholds:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"HealthThresholds": {
"EnableCpuCheck": true,
"EnableMemoryCheck": true,
"EnableDiskCheck": true,
"EnableQueueCheck": true,
"MaxCpuPercentage": 85.0,
"MaxMemoryPercentage": 90.0,
"TotalSystemRamGb": 16.0,
"MinDiskSpacePercentage": 10.0,
"MaxQueueLength": 50.0,
"AppPoolName": "HealthCheckPool"
}
}- EnableCpuCheck: Set to
trueto monitor CPU usage (falseto disable). - EnableMemoryCheck: Set to
trueto monitor RAM usage (falseto disable). - EnableDiskCheck: Set to
trueto monitor free disk space (falseto disable). - EnableQueueCheck: Set to
trueto monitor IIS HTTP request queue size (falseto disable).
- MaxCpuPercentage: HTTP 503 is returned if server CPU usage exceeds this threshold (e.g.
85.0%). - MaxMemoryPercentage: HTTP 503 is returned if server Memory usage exceeds this threshold (e.g.
90.0%). - MinDiskSpacePercentage: HTTP 503 is returned if system drive (
C:) free disk space falls below this threshold (e.g.10.0%). - MaxQueueLength: HTTP 503 is returned if IIS
http.sysrequest queue length exceeds this count (e.g.50). - AppPoolName: Target IIS Application Pool name monitored for request queue size (e.g.
HealthCheckPool).
graph TD
A[Open IIS Manager] --> B[Create App Pool: HealthCheckPool]
B --> C[Set CLR to No Managed Code]
C --> D[Set AlwaysRunning & Idle Timeout 0]
D --> E[Add Website on Port 8080]
E --> F[Point Physical Path to C:\inetpub\HealthCheckSidecar]
- Open IIS Manager (
inetmgr). - Right-click Application Pools -> Add Application Pool...
- Name:
HealthCheckPool - Set .NET CLR version to No Managed Code (required for ASP.NET Core In-Process hosting).
- Click OK.
- Select
HealthCheckPool, click Advanced Settings...:- Change Start Mode from
OnDemandtoAlwaysRunning. - Change Idle Time-out (minutes) from
20to0.
- Change Start Mode from
- Click OK.
- Right-click Sites -> Add Website...
- Site name:
HealthCheckSidecar - Application pool:
HealthCheckPool - Physical path:
C:\inetpub\HealthCheckSidecar - Binding: Set Port to
8080(recommended default, or any available custom port like8081if8080is in use). - Click OK.
Note
Port Selection Flexibility: Port 8080 is documented as the recommended default. If port 8080 is already in use by another application on your server, simply assign any available port (such as 8081, 8088, or 9090) in IIS Manager and configure the matching port in BIG-IP as the Alias Service Port.
- Open a browser on the server or run
curl:curl -i http://localhost:8080/api/health
- Response when Healthy (
HTTP 200 OK):{ "status": "Healthy", "cpu": "12.4%", "memory": "48.2%", "diskFree": "35.8%", "queueLength": 0, "reason": "Healthy" } - If CPU, Memory, Disk Space, or Request Queue exceeds thresholds, the endpoint automatically returns
HTTP 503 Service Unavailable:{ "status": "Unhealthy", "cpu": "89.1%", "memory": "48.2%", "diskFree": "35.8%", "queueLength": 0, "reason": "Threshold breached (CPU: 89.1% > 85%)" }
- In IIS Manager, select
HealthCheckSidecar. - Double-click IP Address and Domain Restrictions (requires
Web-IP-Securityfeature). - Click Add Allow Entry... and add your BIG-IP Self-IP addresses.
- Click Edit Feature Settings... and set Access for unspecified clients to
ForbiddenorAbort.
When your backend application servers run on web ports (such as HTTP 80, HTTPS 443, or a custom application port), but your health check sidecar is hosted on port 8080 (or your chosen custom management port), you configure the BIG-IP health monitor using an Alias Service Port.
For complete end-to-end reliability, it is recommended to use a Dual-Monitor Strategy on BIG-IP:
- Sidecar Probe (Port 8080 or custom sidecar port): Monitors OS-level CPU and RAM performance.
- Application Probe (Port 80, 443, or custom application port): Probes the actual web application endpoint to verify application layer health.
sequenceDiagram
autonumber
actor Client
participant F5 as F5 BIG-IP
participant IIS as IIS 10 Server
rect rgb(240, 248, 255)
note right of F5: Active Monitoring Channel (Dual Probes)
F5->>IIS: Probe 1: OS Resource Probe (Sidecar Port e.g. 8080 GET /api/health)
IIS-->>F5: HTTP/1.1 200 OK (CPU & Memory Healthy)
F5->>IIS: Probe 2: Web Application Probe (App Port e.g. 80/443/custom GET /)
IIS-->>F5: HTTP/1.1 200 OK (Application Layer Healthy)
end
rect rgb(240, 255, 240)
note right of F5: Production Traffic Channel
Client->>F5: Request Site (App Port e.g. 80/443/custom)
F5->>IIS: Forward connection to Pool Member (App Port e.g. 80/443/custom)
end
- Log in to the BIG-IP Configuration Utility (GUI).
- Navigate on the left menu to: Local Traffic ➡️ Monitors.
- Click the Create... button in the upper-right corner.
- Configure the settings precisely as follows:
- Name:
mon_iis10_sidecar_8080(ormon_iis10_sidecar_<port>) - Type:
HTTP - Send String:
GET /api/health HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n - Receive String:
200 OK - Alias Address:
* All Addresses(wildcard automatically targets each pool member's IP address) - Alias Service Port:
8080(or set to your custom IIS sidecar port, e.g.8081)
- Name:
- Click Finished to save the monitor.
Tip
Best Practice — Recommended Dual-Monitor Strategy: Combining the IIS Health Check sidecar monitor with a standard application health monitor ensures complete coverage:
- Server OS Health Monitor (
mon_iis10_sidecar_8080): Ensures the Windows Server host has sufficient CPU and RAM headroom (probed via the sidecar port, e.g.,8080). - Application Health Monitor (
mon_http_app/mon_https_app): Ensures the web application itself is active and serving healthy responses on its primary application port — whether standard HTTP (80), HTTPS (443), or a custom application port (8080,8443,5000, etc.).
Requiring both monitors to pass guarantees client traffic is routed only to servers that are both physically healthy and functionally healthy.
- Navigate in BIG-IP to: Local Traffic ➡️ Pools ➡️ Pool List.
- Click on your active web application pool (containing your IIS members listening on Port 80, 443, or your application port).
- On the Properties tab, locate the Health Monitors section.
- In the Available list, select both monitors:
mon_iis10_sidecar_8080(OS CPU/RAM sidecar monitor)mon_http/mon_https_app_check(Application endpoint monitor)
- Click the << (Add) button to move both monitors into the Active list.
- Verify Availability Requirement is set to All (default
ANDrule, requiring both monitors to pass). - Click Update at the bottom of the page.
When utilizing dual health monitors on a BIG-IP pool member:
- Resource Threshold Breach (High CPU/RAM): The sidecar probe on Port
8080(or custom sidecar port) receivesHTTP 503 Service Unavailable. BIG-IP marks the member down, protecting the server from crashing under load spikes. - Application Failure (App Crash / DB Down): The application probe on the application port (e.g.,
80,443, or custom app port) receives anHTTP 50xerror or connection timeout. BIG-IP marks the member down, preventing users from experiencing broken application pages. - Member Health Standard: Production traffic is forwarded to a pool member only when both probes pass (
HTTP 200 OK), providing complete end-to-end fault tolerance.
Module: IIS Web Core
Error Code: 0x8007000d
Config File: \\?\C:\inetpub\HealthCheckSidecar\web.config
Error Code 0x8007000d (ERROR_INVALID_DATA) occurs because IIS does not recognize the <aspNetCore> section in web.config. This happens when the .NET Core Hosting Bundle (AspNetCoreModuleV2) is not installed on the Windows Server, or IIS was not restarted after installation.
- Install .NET Core Hosting Bundle:
- Download and run the .NET 8.0 Hosting Bundle Installer.
- Restart IIS Services:
- Open Command Prompt as Administrator and run:
net stop was /y net start w3svc
- Open Command Prompt as Administrator and run:
- Verify Module Registration:
- Open PowerShell as Administrator and run:
Get-WebGlobalModule | Where-Object { $_.Name -eq "AspNetCoreModuleV2" }
- If registered, refresh the site in IIS Manager. The 500.19 error will be resolved.
- Open PowerShell as Administrator and run:
