How to Configure Windows Performance Monitor (perfmon) on Windows Server 2025
Windows Performance Monitor — accessed via perfmon.exe — is the built-in tool for capturing detailed system performance data on Windows Server 2025 without installing any third-party software. It can record hundreds of performance counters including CPU utilisation, memory pressure, disk throughput, network I/O, and application-specific counters exposed by roles like IIS or SQL Server. Used effectively, perfmon helps you establish performance baselines, investigate intermittent slowdowns after the fact using saved logs, and generate formatted reports for capacity planning. This guide covers the full workflow from adding live counters to scheduling automated Data Collector Sets, converting log formats, and using Resource Monitor for real-time drill-down.
Prerequisites
- Windows Server 2025 with Administrator or Performance Monitor Users group membership
- Sufficient disk space on the log destination volume (plan for at least several GB for extended captures)
- Basic familiarity with Windows performance concepts (processes, threads, working set)
- PowerShell 5.1 or later for scripted counter capture and conversion tasks
Step 1: Open Performance Monitor and Add Counters
Launch Performance Monitor from Run (Win+R → perfmon) or from Server Manager → Tools. The default view shows the % Processor Time counter on the real-time graph. To add counters:
- Click the + (Add) button in the toolbar, or press
Ctrl+I - In the Add Counters dialog, expand a category (e.g., Memory)
- Select the counter (e.g., Available MBytes) and click Add >>
- Click OK to return to the graph
You can also add counters from PowerShell for quick spot checks without opening the GUI:
# Sample a set of performance counters once
$counters = @(
"Processor(_Total)% Processor Time",
"MemoryAvailable MBytes",
"MemoryPages/sec",
"PhysicalDisk(_Total)Avg. Disk Queue Length",
"PhysicalDisk(_Total)Disk Transfers/sec",
"Network Interface(*)Bytes Total/sec",
"SystemProcessor Queue Length",
"SystemContext Switches/sec"
)
Get-Counter -Counter $counters |
Select-Object -ExpandProperty CounterSamples |
Select-Object Path, CookedValue |
Format-Table -AutoSize
Step 2: Switch to Report View
The Counter pane shows a real-time line graph, which is useful for live observation. For a snapshot table of current values, switch to Report view by clicking the report icon in the toolbar (or press Ctrl+T). Report view shows each counter’s current value as a number, which is easier to read when monitoring many counters simultaneously.
To change the graph display interval (how often the chart updates):
# Using PowerShell to continuously sample and display top counters every 5 seconds
$counters = @(
"Processor(_Total)% Processor Time",
"MemoryAvailable MBytes",
"PhysicalDisk(_Total)Avg. Disk Queue Length"
)
while ($true) {
Clear-Host
$sample = Get-Counter -Counter $counters
$sample.CounterSamples | ForEach-Object {
[PSCustomObject]@{
Counter = ($_.Path -split "\")[-1]
Value = [math]::Round($_.CookedValue, 2)
}
} | Format-Table -AutoSize
Start-Sleep -Seconds 5
}
Step 3: Create a Data Collector Set
A Data Collector Set (DCS) is a named collection of performance counters and trace providers that perfmon records to a file on a schedule. This is the primary mechanism for capturing historical performance data for post-incident analysis.
To create one via the GUI: Performance Monitor → Data Collector Sets → User Defined → right-click → New → Data Collector Set. Choose Create manually (Advanced), select Performance counter, then add counters as above.
To create a Data Collector Set entirely from PowerShell using logman:
# Define the counter list
$counterList = @(
"Processor(_Total)% Processor Time",
"Processor(_Total)% Privileged Time",
"MemoryAvailable MBytes",
"MemoryPages/sec",
"MemoryPool Nonpaged Bytes",
"PhysicalDisk(_Total)Avg. Disk Queue Length",
"PhysicalDisk(_Total)Disk Read Bytes/sec",
"PhysicalDisk(_Total)Disk Write Bytes/sec",
"Network Interface(*)Bytes Total/sec",
"SystemProcessor Queue Length",
"SystemContext Switches/sec",
"Process(_Total)Working Set",
"Web Service(_Total)Current Connections",
"Web Service(_Total)Total Method Requests/sec"
)
# Write counter list to a temp file
$counterFile = "C:Tempperfmon_counters.txt"
New-Item -ItemType Directory -Path "C:Temp" -Force | Out-Null
$counterList | Set-Content -Path $counterFile
# Create the Data Collector Set
logman create counter "WS2025-Baseline" `
--cf $counterFile `
-f csv `
-si 00:00:30 `
-o "C:PerfLogsWS2025-Baselinebaseline" `
-max 1024 `
--v
Write-Host "Data Collector Set 'WS2025-Baseline' created"
Step 4: Configure Log Format, Schedule, and Stop Conditions
Perfmon supports several log formats. CSV is human-readable and easy to import into Excel or Power BI. Binary (.blg) is more compact and can be re-read by perfmon. SQL mode writes directly to a database for long-term retention.
# Update existing DCS to binary format with a 512 MB size limit
logman update counter "WS2025-Baseline" -f bincirc -max 512
# Schedule the DCS to start at 08:00 and stop at 18:00 on weekdays
logman update counter "WS2025-Baseline" `
-b 08:00:00 `
-e 18:00:00 `
-rf 10:00:00
# Start the collector immediately
logman start "WS2025-Baseline"
logman query "WS2025-Baseline"
# Stop the collector
# logman stop "WS2025-Baseline"
# List all running collectors
logman query -ets | Select-String "Running"
You can also configure stop conditions in the GUI under DCS → Properties → Stop Condition — set maximum log file size (e.g., 1024 MB) or overall duration (e.g., 24 hours) to prevent unbounded disk consumption.
Step 5: Convert and Analyse Log Files with relog
The relog command-line tool converts perfmon binary logs to other formats and can resample at lower frequency to reduce file size:
# Convert binary (.blg) log to CSV for Excel analysis
relog "C:PerfLogsWS2025-Baselinebaseline_000001.blg" `
-f csv `
-o "C:PerfLogsWS2025-Baselinebaseline_export.csv"
# Resample to 5-minute intervals (reduces file size significantly)
relog "C:PerfLogsWS2025-Baselinebaseline_000001.blg" `
-f csv `
-t 10 `
-o "C:PerfLogsWS2025-Baselinebaseline_5min.csv"
# Extract only specific counters from a large log
relog "C:PerfLogsWS2025-Baselinebaseline_000001.blg" `
-c "Processor(_Total)% Processor Time" `
-c "MemoryAvailable MBytes" `
-f csv `
-o "C:PerfLogsWS2025-Baselinecpu_memory_only.csv"
Write-Host "Log conversion complete"
Step 6: Generate Performance Reports from Data Collector Sets
Perfmon can automatically generate an HTML report from a completed DCS. Right-click a stopped DCS in the GUI and select Latest Report. Reports appear under Performance Monitor → Reports → User Defined → WS2025-Baseline.
# Generate a report from a completed DCS log via PLA (Performance Logs and Alerts) COM object
# This triggers the built-in report generation engine
$pla = New-Object -ComObject Pla.DataCollectorSet
$pla.Query("WS2025-Baseline", $null)
# Check the output directory for generated reports
$reportDir = "C:PerfLogsWS2025-Baseline"
Get-ChildItem -Path $reportDir -Recurse -Include "*.html","*.blg","*.csv" |
Sort-Object LastWriteTime -Descending |
Select-Object Name, Length, LastWriteTime |
Format-Table -AutoSize
Step 7: Use Resource Monitor for Real-Time Diagnostics
While perfmon is ideal for historical logging, Resource Monitor (resmon.exe) provides an interactive real-time view of CPU, memory, disk, and network usage broken down per process. Launch it from perfmon’s Action menu or directly:
# Launch Resource Monitor
Start-Process resmon.exe
# Alternatively, capture a process-level resource snapshot via PowerShell
Get-Process |
Sort-Object CPU -Descending |
Select-Object -First 15 Name, Id, CPU, WorkingSet, PM, NPM |
ForEach-Object {
[PSCustomObject]@{
Name = $_.Name
PID = $_.Id
"CPU(s)" = [math]::Round($_.CPU, 1)
"WS(MB)" = [math]::Round($_.WorkingSet / 1MB, 1)
"PM(MB)" = [math]::Round($_.PM / 1MB, 1)
}
} | Format-Table -AutoSize
Step 8: Key Baseline Counters to Always Capture
When setting up performance baselines for a new Windows Server 2025 deployment, always include this minimum counter set:
$baselineCounters = @(
# CPU
"Processor(_Total)% Processor Time",
"Processor(_Total)% Privileged Time",
"Processor(_Total)% Interrupt Time",
"SystemProcessor Queue Length",
# Memory
"MemoryAvailable MBytes",
"MemoryPages/sec",
"MemoryPage Faults/sec",
"MemoryPool Nonpaged Bytes",
# Disk — enumerate per physical disk
"PhysicalDisk(*)Avg. Disk Queue Length",
"PhysicalDisk(*)Avg. Disk sec/Read",
"PhysicalDisk(*)Avg. Disk sec/Write",
"PhysicalDisk(*)Disk Bytes/sec",
# Logical Disk — per volume
"LogicalDisk(*)% Free Space",
"LogicalDisk(*)Free Megabytes",
# Network
"Network Interface(*)Bytes Total/sec",
"Network Interface(*)Output Queue Length",
# System
"SystemContext Switches/sec",
"SystemSystem Calls/sec"
)
# Capture a 60-second sample set at 5-second intervals and export
Get-Counter -Counter $baselineCounters -SampleInterval 5 -MaxSamples 12 |
Export-Counter -Path "C:PerfLogsBaseline_$(Get-Date -Format 'yyyyMMdd_HHmm').blg" -FileFormat BLG
Write-Host "Baseline capture saved"
Conclusion
Windows Performance Monitor on Windows Server 2025 is a powerful, zero-cost tool for both real-time observation and long-term performance recording. By combining automated Data Collector Sets on a schedule with post-collection analysis via relog and generated reports, you build an audit trail that makes root-cause analysis possible even when a performance problem occurred overnight or over a weekend. Pair perfmon baselines with Resource Monitor for live drill-down into problem processes, and your team will have a comprehensive, native performance visibility toolkit without any third-party agents.