A single Windows Event Collector (WEC) server has three documented scaling limits: disk write speed, concurrent WinRM connections, and registry growth from per-client bookmark data. Microsoft’s current guidance is to plan for roughly 3,000 events per second on average across all subscriptions on commodity hardware.
The "10k × 10k" rule of thumb (up to 10,000 clients and 10,000 EPS per collector) still circulates widely. IBM QRadar and Juniper JSA documentation still quote it, both attributing it to Microsoft’s article. That figure no longer appears in Microsoft’s article as of its August 2025 revision. If your capacity plan assumes 10k × 10k, re-check it against the 3,000 EPS guidance.
What limits WEC scalability?
Microsoft’s Windows Event Forwarding guidance names three factors that cap a WEC server:
- Disk I/O
-
The collector doesn’t process or validate incoming events. It buffers them and writes them to a local EVTX file, so disk write speed sets the throughput ceiling.
- Network connections
-
WEF sources don’t hold permanent connections, but they don’t disconnect immediately after delivering events either. Microsoft ties the ceiling to the TCP ports available on the collector. In practice, WinRM’s own quotas hit the ceiling first. The next section explains why.
- Registry size
-
The collector creates a registry key per client FQDN to store bookmark and heartbeat state. Left unpruned, it keeps growing until management tooling breaks, and eventually the registry itself stops working.
Every failure described in this article comes down to one of these three factors.
Microsoft’s numbers: 3,000 EPS, not 10k × 10k
Here is what Microsoft documents today, with sources:
| Limit | Documented figure | Source (Microsoft) |
|---|---|---|
Sustained throughput |
~3,000 EPS average, total across all subscriptions, commodity hardware |
WEF intrusion detection guidance, updated 2025 |
Clients per collector |
2,000 to 4,000 clients recommended, on at least 16 GB RAM and 4 processors; deploy multiple collectors for 40,000–100,000 sources |
|
Collector memory |
WecSvc memory "may quickly exceed 4 GB and continue to grow" at 4,000 clients with 5–7 subscriptions, making the server unresponsive |
Same performance article |
WinRM concurrency |
MaxConnections default 300; MaxConcurrentOperationsPerUser default 1,500 |
|
Event Viewer usability |
Degrades above 1,000 lifetime sources per subscription; unusable above 50,000 (wecutil only) |
WEF intrusion detection guidance |
Collector survivability |
Above 100,000 lifetime sources, the registry becomes unreadable, and the server likely needs a rebuild |
WEF intrusion detection guidance |
Client-side buffer |
The Security log’s locally configured maximum defaults to 20 MB, and the local log is a WEF client’s only buffer |
Note that the throughput guidance is per collector, not per subscription: five busy subscriptions share one 3,000 EPS budget. And "lifetime sources" counts every client that has ever connected, not just active ones, so a fleet with normal hardware churn crosses the thresholds far sooner than its active count suggests.
For context on what happens downstream, the same Microsoft article maps event volume to storage:
-
Up to 5,000 EPS fits SQL Server or a security event manager (SEM).
-
5,000 to 50,000 EPS calls for a dedicated SEM.
-
50,000+ EPS points to Hadoop, HDInsight, or data-lake storage.
A single WEC server sits at the bottom of that scale.
Where a WEC server breaks in practice
One EVTX file serializes your disk writes
By default, every subscription goes to a single ForwardedEvents log. Microsoft recommends fast disks and moving that log to another disk. An advanced approach is to split subscriptions further across custom event channels so writes spread across multiple EVTX files. Palantir documented this approach in 2018 and published reusable channel manifests in their windows-event-forwarding repository.
Also check the log size before traffic arrives.
On a default install, wevtutil gl ForwardedEvents reports a 20 MB maximum, and a 20 MB log wraps in minutes under enterprise volume.
The resize command is in the tuning steps below.
WinRM quotas cap concurrent connections before TCP ports do
WEF rides on WinRM. Microsoft attributes the connection ceiling to available TCP ports, but WinRM’s own quotas are the lower number: the documented MaxConnections default is 300, against an ephemeral port range in the tens of thousands. Thousands of clients checking in on short heartbeat intervals queue behind that limit long before the server runs out of ports.
One caveat when you check this yourself: the same Microsoft page lists 300 as the default and then gives 25 as the WinRM 2.0 default.
Confirm what your collector reports with winrm get winrm/config before planning around either figure.
Connection frequency depends on three parameters: the Refresh interval in the GPO URL, plus DeliveryMaxLatency and HeartbeatInterval in the subscription.
Microsoft covers all three in its forwarding performance article.
Registry bloat degrades, then kills, the collector
Each client that touches a subscription gets a key under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\EventCollector\Subscriptions\<name>\EventSources holding its bookmark and last heartbeat.
Microsoft documents a clear degradation curve as that count grows:
-
Past 1,000 lifetime sources, Event Viewer hangs for minutes on the Subscriptions node.
-
Past 50,000, you manage subscriptions with
wecutilonly. -
Past 100,000, the registry becomes unreadable, and you rebuild the server.
Pruning stale sources is a permanent operational chore.
The community-maintained wef-guidance repository ships a Remove-StaleWECSubscribers.ps1 scheduled-task script for exactly this reason.
Delivery mode trades latency for connection load
The built-in delivery options are a three-way trade:
| Mode | Transport | Batch timeout | Practical effect |
|---|---|---|---|
Normal |
Pull |
5 items / 15 minutes |
Reliable, cheap, and 15 minutes late |
Minimize bandwidth |
Push |
6 hours |
Fine for compliance archives, useless for detection |
Minimize latency |
Push |
30 seconds |
What SecOps wants |
A detection use case forces you into Minimize Latency, and Minimize Latency opens the most concurrent connections, so it collides with the WinRM quotas above.
If you need finer control, you can use Custom mode, configurable only through wecutil.
Rendered text doubles or triples event size
WEF’s default RenderedText format ships the full human-readable description with every event. Microsoft’s guidance states this effectively doubles or triples event size, and that switching the subscription to the binary Events format can more than double the volume a single collector accommodates. It’s a one-line change (shown below) and the cheapest capacity win available.
Silent loss, duplicate HA, and a Server 2019 gotcha
Microsoft documents all three behaviors:
- Silent client-side loss
-
A WEF client’s only buffer is its local event log. The Security log’s locally configured maximum defaults to 20 MB, and the Application and System logs default to just 1 MB. Hence, subscriptions drawing on those channels have a far smaller cushion than the Security figure suggests. If the collector is unreachable long enough for a log to wrap, the overwritten events are gone. Microsoft’s article is explicit: it does not send a notification, and no gap indicator exists in the stream.
- "High availability" means duplication
-
The supported HA pattern is pointing clients at two collectors simultaneously. Every event arrives twice, deduplication becomes your SIEM’s problem, and every duplicate counts against volume-based SIEM licensing.
- Windows Server 2019 broke the default config
-
On Server 2019 machines with more than 3.5 GB of RAM, WinRM and WecSvc split into separate service host processes, which can stop forwarding entirely until you fix URL ACLs. If a collector receives nothing after an upgrade, start there.
Tuning a native WEC as far as it goes
If you’re staying on native WEC, these changes extract most of the available headroom. Run everything from an elevated prompt on the collector.
1. Set up the collector and a source-initiated subscription.
<!-- security-baseline.xml: minimal source-initiated subscription -->
<Subscription xmlns="http://schemas.microsoft.com/2006/03/windows/events/subscription">
<SubscriptionId>SecurityBaseline</SubscriptionId>
<SubscriptionType>SourceInitiated</SubscriptionType>
<Enabled>true</Enabled>
<Uri>http://schemas.microsoft.com/wbem/wsman/1/windows/EventLog</Uri>
<ConfigurationMode>MinLatency</ConfigurationMode>
<Query><![CDATA[
<QueryList>
<Query Id="0">
<Select Path="Security">*[System[(EventID=4624 or EventID=4625 or EventID=4688)]]</Select>
<Select Path="Microsoft-Windows-Sysmon/Operational">*</Select>
</Query>
</QueryList>
]]></Query>
<LogFile>ForwardedEvents</LogFile>
<!-- Microsoft's documented default: grants Domain Computers and the local Network Service -->
<AllowedSourceDomainComputers>O:NSG:NSD:(A;;GA;;;DC)(A;;GA;;;NS)</AllowedSourceDomainComputers>
</Subscription>
> wecutil qc /q
> wecutil cs security-baseline.xml
2. Switch to binary event format and tune delivery. All four commands come straight from Microsoft’s WEF article:
> wecutil ss "SecurityBaseline" /cf:Events (1)
> wecutil ss "SecurityBaseline" /cm:Custom (2)
> wecutil ss "SecurityBaseline" /dmi:1
> wecutil ss "SecurityBaseline" /dmlt:10
| 1 | Binary "Events" format drops rendered text and can more than double capacity. |
| 2 | Custom delivery: sends after 1 event or 10 ms, whichever comes first. |
Note the trade-off: tighter /dmlt values mean more frequent client connections, so pair aggressive latency settings with the WinRM quota review above.
3. Resize the destination log before it wraps.
> wevtutil gl ForwardedEvents
> wevtutil sl ForwardedEvents /ms:4294967296 (1)
| 1 | Raises the cap to 4 GB (value in bytes). |
4. Watch your lifetime source count against Microsoft’s thresholds.
$base = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\EventCollector\Subscriptions'
Get-ChildItem $base | ForEach-Object {
$sources = @(Get-ChildItem "$($_.PSPath)\EventSources" -ErrorAction SilentlyContinue)
[pscustomobject]@{ Subscription = $_.PSChildName; LifetimeSources = $sources.Count }
} | ForEach-Object {
$_
if ($_.LifetimeSources -gt 50000) {
Write-Warning "$($_.Subscription): past Microsoft's 50k threshold - Event Viewer unusable, wecutil only"
}
elseif ($_.LifetimeSources -gt 1000) {
Write-Warning "$($_.Subscription): past 1k - expect Event Viewer delays"
}
}
Schedule stale-source pruning alongside this check. The wef-guidance scripts are a solid starting point.
When tuning stops helping
Tuning raises the ceiling but doesn’t remove it. Writes still serialize into EVTX files, the registry still grows with every client, and HA still means paying to ingest everything twice. Past that point, adding collectors multiplies the per-box chores and still leaves a log shipper on every collector to move events off the EVTX files. Before adding a third collector, ask whether a Windows collector belongs in the path at all.
Scaling past WEC with NXLog Agent
You can keep everything you like about WEF (agentless clients, GPO-driven enrollment, Kerberos authentication) and replace what doesn’t scale. Or you can remove WEF from the path entirely. NXLog Agent supports both.
Option 1: keep WEF, replace the collector
NXLog Agent can act as the Windows Event Collector itself through its Windows Event Collector input module, accepting source-initiated WEF pushes with Kerberos or certificate authentication. Your WEF clients and GPOs don’t change. What changes on the collector side:
- It runs on Linux, macOS, or Windows
-
The Windows Server license and the Windows-specific failure modes above leave the collection tier. Our step-by-step guide to a WEC on Linux covers Kerberos and certificate setups.
- Events enter a processing pipeline, not an EVTX file
-
Received events flow straight into NXLog Agent routes, where you can trim, filter, and normalize them before they reach your SIEM, instead of writing to ForwardedEvents and re-reading it with a separate shipper.
- Subscription logic lives in one readable config
-
Per-client queries are plain QueryXML blocks:
<Input wec>
Module im_wseventing
ListenAddr 0.0.0.0
Port 5985
Address http://wec.example.com:5985/wsman
<QueryXML>
<QueryList>
<Query Id="0">
<Select Path="Security">*[System[(EventID=4624 or EventID=4625 or EventID=4688)]]</Select>
<Select Path="Microsoft-Windows-Sysmon/Operational">*</Select>
</Query>
</QueryList>
</QueryXML>
</Input>
We don’t publish a per-collector EPS benchmark for the Windows Event Collector input module, because throughput depends on your event mix, filtering, and outputs. We field-tested NXLog Agent at volumes over 100,000 events per second, the figure our documentation cites for the product overall. Treat any vendor number, ours included, as a starting point, and load-test with your own subscriptions before you commit.
Option 2: take WEF out of the path
At sufficient scale, the intermediary is the problem. Installing NXLog Agent on endpoints and collecting events locally with the Event Log for Windows input module removes the WEF hop, and with it the collector bottleneck, the registry bookkeeping, and duplicate-ingest HA. The same XPath queries you wrote for subscriptions work in the NXLog Agent configuration:
<Input security_events>
Module im_msvistalog
<QueryXML>
<QueryList>
<Query Id="0">
<Select Path="Security">*[System[(EventID=4624 or EventID=4625 or EventID=4688)]]</Select>
</Query>
</QueryList>
</QueryXML>
</Input>
<Output siem>
Module om_tcp
Host siem.example.com:1514
</Output>
<Route security_events_to_siem>
Path security_events => siem
</Route>
The usual objection to agents is fleet management, and that’s what NXLog Platform exists for. You enroll agents centrally, push configuration from templates, and monitor per-agent status and throughput. On a native WEC, the only trace of a client that stopped forwarding events is a registry timestamp you have to look for. The NXLog Platform documentation covers both deployment models. Mixing them is normal: agents where you can install them, Windows Event Collector instances for the segments where you can’t.
Which architecture fits?
| Native WEC + shipper | NXLog Agent as WEC | NXLog Agent + NXLog Platform | |
|---|---|---|---|
Client-side footprint |
None (GPO only) |
None (GPO only) |
Agent per endpoint |
Collector OS |
Windows Server only |
Linux, macOS, or Windows |
No collector required |
Throughput ceiling |
~3,000 EPS per collector (Microsoft guidance) |
Pipeline-dependent; load-test |
Scales with endpoints; no central bottleneck |
Filtering before SIEM |
Subscription XPath only |
XPath + full NXLog Agent processing |
XPath + full NXLog Agent processing |
Client health visibility |
Registry timestamps, wecutil |
NXLog Agent logs and stats |
Per-agent status and EPS in NXLog Platform |
Ongoing chores |
Registry pruning, log resizing, per-box tuning |
Standard NXLog Agent operations |
Centralized via NXLog Platform |
FAQ
- How many clients can one WEC server handle?
-
Microsoft’s performance guidance recommends 2,000 to 4,000 clients per collector, backed by at least 16 GB of RAM and four processors, with multiple collectors for fleets of 40,000 to 100,000 sources. It also warns that at 4,000 clients with 5 to 7 subscriptions, collector service memory can exceed 4 GB and make the server unresponsive. For throughput, plan around 3,000 EPS total per collector.
- Is the 10k × 10k rule for WEC still valid?
-
The 10,000-clients / 10,000-EPS rule no longer appears in Microsoft’s WEF article, which now advises planning for ~3,000 EPS per collector. Third-party documentation from IBM and Juniper still quotes the older figure, so check which guidance your capacity plan inherited.
- Does WEC support load balancing or clustering?
-
No. The documented high-availability pattern is configuring clients to forward the same events to multiple collectors simultaneously, which duplicates every event and shifts deduplication downstream.
- What happens to events when the WEC server is offline?
-
Clients buffer their events locally and resume from a bookmark on reconnection. If a local log wraps while the collector is down, it silently overwrites and loses events. By default, the Security log caps out at 20 MB, and Microsoft documents that no notification or gap indicator exists.
- Can I run a Windows Event Collector on Linux?
-
Yes. NXLog Agent’s Windows Event Collector input module implements the collector role of WS-Eventing on Linux, macOS, and Windows, accepting standard source-initiated WEF subscriptions with Kerberos or certificate authentication.