How to Monitor Windows Server with Prometheus and Grafana on Windows Server 2025
Modern infrastructure observability relies on open-source tools that collect, store, and visualize time-series metrics at scale. Prometheus and Grafana have become the de facto standard monitoring stack for both Linux and Windows environments, and Windows Server 2025 is fully supported through the windows_exporter agent maintained by the Prometheus community. This guide walks through a complete end-to-end setup: installing windows_exporter as a native Windows service, configuring Prometheus to scrape it, wiring Grafana to Prometheus, importing a ready-made Windows dashboard, writing useful PromQL queries, and building alert rules that notify you before a disk fills or a critical service drops offline.
Prerequisites
- Windows Server 2025 (the target machine to be monitored)
- A separate Prometheus server (Linux or Windows) with Prometheus 2.x installed and reachable on TCP 9090
- Grafana 10.x or later installed on the same or a different host, reachable on TCP 3000
- Administrator access on the Windows Server 2025 machine
- PowerShell 5.1 or PowerShell 7.x available
- Outbound HTTP access from the Windows server to the Prometheus scrape endpoint (or inbound access from Prometheus to port 9182 on the Windows server)
Step 1: Download and Install windows_exporter
The windows_exporter (formerly wmi_exporter) is a Prometheus exporter for Windows metrics. It exposes hundreds of metrics via an HTTP endpoint on port 9182 that Prometheus scrapes on a configurable interval.
Download the latest MSI installer from the Prometheus community GitHub releases page. You can do this directly from PowerShell:
# Download the latest windows_exporter MSI
$version = "0.27.2"
$url = "https://github.com/prometheus-community/windows_exporter/releases/download/v$version/windows_exporter-$version-amd64.msi"
$dest = "C:Tempwindows_exporter.msi"
New-Item -ItemType Directory -Path "C:Temp" -Force | Out-Null
Invoke-WebRequest -Uri $url -OutFile $dest -UseBasicParsing
Write-Host "Downloaded windows_exporter $version to $dest"
Install the MSI and specify which collectors to enable at install time. The following collectors cover the most critical server metrics:
# Install windows_exporter with specific collectors enabled
$collectors = "cpu,cs,logical_disk,net,os,process,service,system,iis"
Start-Process msiexec.exe -ArgumentList @(
"/i", "C:Tempwindows_exporter.msi",
"/qn",
"ENABLED_COLLECTORS=$collectors",
"LISTEN_PORT=9182"
) -Wait -NoNewWindow
Write-Host "windows_exporter installed successfully"
Verify the service is running:
Get-Service -Name "windows_exporter" | Select-Object Name, Status, StartType
# Test the metrics endpoint
Invoke-WebRequest -Uri "http://localhost:9182/metrics" -UseBasicParsing |
Select-Object -ExpandProperty Content |
Select-String "windows_cpu_time_total" |
Select-Object -First 3
Step 2: Configure the Windows Firewall
Prometheus needs inbound access to TCP port 9182 on the Windows server. Create a firewall rule to allow this:
# Allow Prometheus to scrape windows_exporter
New-NetFirewallRule `
-DisplayName "Prometheus windows_exporter" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 9182 `
-Action Allow `
-Profile Domain,Private `
-Description "Allows Prometheus scraper to collect Windows metrics"
# Optionally restrict to the Prometheus server IP
# -RemoteAddress "192.168.1.50"
Write-Host "Firewall rule created for port 9182"
Step 3: Configure Prometheus to Scrape Windows Server
On your Prometheus server, edit prometheus.yml to add a scrape job for the Windows target. Locate the scrape_configs section and add the following:
scrape_configs:
- job_name: "windows_server_2025"
scrape_interval: 30s
scrape_timeout: 20s
static_configs:
- targets:
- "192.168.1.100:9182" # Replace with your server's IP
labels:
environment: "production"
os: "windows_server_2025"
hostname: "WS2025-01"
Reload Prometheus to apply the configuration:
# On the Prometheus host (Linux example via curl, or PowerShell on Windows Prometheus)
# curl -X POST http://localhost:9090/-/reload
# Verify the target appears as UP in Prometheus
Invoke-WebRequest -Uri "http://prometheus-server:9090/api/v1/targets" -UseBasicParsing |
ConvertFrom-Json |
Select-Object -ExpandProperty data |
Select-Object -ExpandProperty activeTargets |
Where-Object { $_.labels.job -eq "windows_server_2025" } |
Select-Object scrapeUrl, health, lastError
Step 4: Add Prometheus as a Grafana Data Source
Open Grafana in your browser (typically http://grafana-server:3000), log in, then navigate to Connections → Data sources → Add new data source. Select Prometheus and configure:
- Name: Prometheus-Windows
- URL:
http://prometheus-server:9090 - Scrape interval: 30s
Click Save & test. You should see “Successfully queried the Prometheus API.”
Step 5: Import the Windows Dashboard
Grafana dashboard ID 14694 provides a comprehensive pre-built Windows Server overview. Import it via the Grafana API or UI:
# Import dashboard 14694 via Grafana API
$grafanaUrl = "http://grafana-server:3000"
$apiKey = "glsa_YOURSERVICEACCOUNTTOKEN"
$datasourceUid = "YOUR_PROMETHEUS_UID" # Get from Grafana → Data sources
$body = @{
dashboard = @{
id = $null
uid = $null
}
folderId = 0
overwrite = $true
inputs = @(
@{
name = "DS_PROMETHEUS"
type = "datasource"
pluginId = "prometheus"
value = $datasourceUid
}
)
} | ConvertTo-Json -Depth 5
# Download dashboard from Grafana.com
$dashJson = Invoke-RestMethod -Uri "https://grafana.com/api/dashboards/14694/revisions/latest/download"
$body = @{
dashboard = $dashJson
folderId = 0
overwrite = $true
} | ConvertTo-Json -Depth 20
Invoke-RestMethod -Uri "$grafanaUrl/api/dashboards/import" `
-Method Post `
-Headers @{ "Authorization" = "Bearer $apiKey"; "Content-Type" = "application/json" } `
-Body $body
Step 6: Key PromQL Queries for Windows Metrics
Once data is flowing, use these PromQL expressions to build panels or explore in Grafana’s Explore view:
# These are PromQL queries — paste them into Grafana Explore or panel editor
# CPU usage percentage (all cores, 5-minute average)
# 100 - (avg by (instance) (rate(windows_cpu_time_total{mode="idle"}[5m])) * 100)
# Available memory in GB
# windows_os_physical_memory_free_bytes / 1024 / 1024 / 1024
# Disk free percentage per volume
# 100 * (windows_logical_disk_free_bytes / windows_logical_disk_size_bytes)
# Network receive rate (bytes/sec)
# rate(windows_net_bytes_received_total[5m])
# Network transmit rate (bytes/sec)
# rate(windows_net_bytes_sent_total[5m])
# Service state (1 = running, 0 = stopped)
# windows_service_state{state="running"}
# Process CPU usage (top 5 processes)
# topk(5, rate(windows_process_cpu_time_total[5m]))
# System uptime in days
# (time() - windows_system_system_up_time) / 86400
Step 7: Configure Prometheus Alert Rules
Create an alert rules file on your Prometheus server to fire when disk space is critically low or a Windows service goes down:
# /etc/prometheus/rules/windows_alerts.yml
groups:
- name: windows_server_alerts
interval: 1m
rules:
- alert: WindowsDiskSpaceCritical
expr: |
100 * (windows_logical_disk_free_bytes{volume!~"HarddiskVolume.*"}
/ windows_logical_disk_size_bytes{volume!~"HarddiskVolume.*"}) < 10
for: 5m
labels:
severity: critical
annotations:
summary: "Disk space critical on {{ $labels.instance }}"
description: "Volume {{ $labels.volume }} has only {{ $value | printf "%.1f" }}% free space remaining."
- alert: WindowsDiskSpaceWarning
expr: |
100 * (windows_logical_disk_free_bytes{volume!~"HarddiskVolume.*"}
/ windows_logical_disk_size_bytes{volume!~"HarddiskVolume.*"}) 90
for: 10m
labels:
severity: warning
annotations:
summary: "High CPU on {{ $labels.instance }}"
description: "CPU usage is {{ $value | printf "%.1f" }}% for more than 10 minutes."
Reference this file in prometheus.yml under the rule_files section and reload Prometheus to activate alerts.
Conclusion
You now have a fully operational observability pipeline for Windows Server 2025 using Prometheus and Grafana. The windows_exporter agent feeds rich Windows performance data — CPU, memory, disk, network, services, and processes — into Prometheus, where it is stored as time-series data and made available for Grafana dashboards and alert rules. The combination of the pre-built dashboard 14694 and the custom PromQL alert rules ensures both at-a-glance visibility and proactive notification when thresholds are breached. As your environment grows, extend the scrape targets section with additional Windows hosts and refine alert thresholds based on your observed baselines.