Windows Defender on Windows Server: Exclusions, Scan Load, and Where It Costs CPU

Windows Defender is enabled by default on every Windows Server 2016 and later image, and on a small VPS it is the one background process most operators never inspect. On a 2 vCPU / 4 GB IIS instance it adds 3-9 percent sustained CPU when the site serves a few hundred small files a minute, with 25-40 percent spikes when a CMS unpacks a plugin, extracts a zip or rebuilds a cache directory. The fix is not to disable it. It is to scope it: tell it which folders never need scanning, move full scans where they cannot overlap peak traffic, and prove with commands that the exclusions took effect. That is the work covered below for a typical Windows VPS hosting setup where a shared vCPU and 60-120 GB of SSD are the whole budget.

What real-time protection costs on 2 vCPUs

Real-time protection hooks file I/O through the filter manager. Each create, write and execute that passes it costs microseconds when the file is cached and far more when it is not, because the engine reads the file, hashes it, and checks signature and cloud metadata. On a desktop that is invisible. On two shared vCPUs it is direct competition for the cores IIS uses.

The worst case is bulk file churn rather than steady traffic. A WordPress plugin update unpacks hundreds of files, Composer or NuGet restores thousands, and a nightly backup rewrites a whole site tree. Each produces a scan queue that pegs one core for 30-90 seconds, which surfaces as request latency rather than any warning. Watch MsMpEng in Performance Monitor or run Get-Process MsMpEng | Select CPU,WorkingSet during a plugin update and compare it to a quiet period.

The IIS and inetpub exclusion set

IIS reads static files from C:\inetpub on every request and appends to its log directories continuously. Scanning those on read gains almost nothing: uploaded content should be scanned when it arrives, and static assets are already known to the server. The standard set for an IIS host is short, and it is worth applying every entry as both a path and a process where one exists.

  • C:\inetpub\temp – compression and ASP.NET temporary files, rewritten constantly
  • C:\inetpub\logs\LogFiles – W3C logs written on every hit
  • C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files
  • Process exclusions for w3wp.exe and appcmd.exe; path exclusions alone do not stop process-level scans
  • Only your own deployment folders under wwwroot, never the whole wwwroot, and never the uploads path

SQL Server exclusions that matter

SQL Server is the other half of the problem. Its data, log and backup files are written in large sequential blocks that Defender inspects on every write, and Microsoft publishes a specific exclusion list for it. Start with *.mdf, *.ldf, *.ndf and *.bak plus the directories holding them, typically D:\SQLData and D:\SQLLog on a second data disk.

Then exclude the processes: sqlservr.exe, sqlwriter.exe and bcm.exe. A path exclusion does not cover memory-mapped engine activity; the process exclusion is what removes the per-write hook cost. Apply the same pattern to mysqld.exe if you run MySQL, excluding *.ibd, *.ibd.delta and the binlog directory. On two vCPUs these entries are often worth 10-20 points of write throughput on a mid-sized database.

Custom scan schedules instead of the defaults

The default schedule is a quick scan in the early hours plus a full scan every seven days, both at a time the OS picks. Quick scans finish in minutes and touch only high-risk locations; a full scan over 60 GB takes 40-90 minutes and hammers the disk. Move it to a window you can defend and cap the CPU share: Set-MpPreference -ScanScheduleDay Sunday -ScanScheduleTime 04:00 -ScanParameters FullScan -ScanAvgCPULoadFactor 25.

The 25 percent cap keeps a background full scan from competing with evening web traffic. Keep it at least two hours away from the backup window so the scanner and the backup tool are not both walking the same tree, and check Get-MpComputerStatus | Select FullScanStartTime,FullScanEndTime the next morning to confirm the run actually finished inside the window rather than being cancelled at the throttled pace.

ASR rules on Server

Attack Surface Reduction rules catch what exclusions skip: Office apps spawning script interpreters, credential theft from LSASS, email clients writing executables, and unsigned binaries launched from Temp. Server does not apply the desktop Office rules automatically, so you enable them by GUID through Group Policy or PowerShell. A practical start is executable content from email, Office child processes, credential stealing from lsass.exe (GUID 9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2), untrusted USB executables and WMI event-subscription persistence.

Run them in Audit for one week, check Get-WinEvent -LogName "Microsoft-Windows-Windows Defender/Operational" -MaxEvents 200 for what would have been blocked, then switch to Block. On a VPS with no mail server the email rules cost nothing. Do not enable “block JavaScript from downloading executable content” if your build pipeline does exactly that – it will break deploys at the worst moment.

Definition updates and the bandwidth they consume

Signature packages land three to five times a day at 30-90 MB per delta, and platform updates every few weeks are 200 MB or more. On a Windows RDP VPS with a capped monthly transfer that is 3-6 GB a month spent on signatures alone. Most providers do not bill inbound, but each definition change also triggers a fresh evaluation pass over recently touched files, which shows up as a short CPU bump.

Verify freshness with Get-MpComputerStatus | Select AntivirusSignatureLastUpdated and keep signature age under 24 hours. Behind a proxy, point Defender at WSUS with Set-MpPreference -SignatureFallbackOrder MicrosoftUpdateServer,FileShare.

What third-party antivirus adds and removes

Commercial engines add central management, application control and a fleet console. On one VPS those features go unused. What they remove is more concrete: another 150-400 MB of RAM, a second kernel filter driver with its own update cadence, and a renewal cost that can approach a month of the VPS. Many also conflict with Defender’s filters unless registered as the primary AV.

If you install one, confirm Defender goes passive with Get-MpComputerStatus | Select AMRunningMode (Passive or Normal, never Off) and keep periodic scanning on so an expired licence does not leave the server bare. For a single-instance IIS host, Defender plus ASR rules plus a hardened RDP configuration is usually the better trade than a paid engine.

Verification commands to prove the config took

Exclusions fail silently, and a typo in a path is accepted without complaint. Check Get-MpPreference | Select -ExpandProperty ExclusionPath and ExclusionProcess, which returns what the service really uses rather than what the policy file says. Then confirm health: Get-MpComputerStatus gives engine version, signature age, real-time status and last scan times, while Get-MpThreatDetection lists recent hits with paths so you can see whether an exclusion is hiding real activity.

If the settings came from Group Policy, run gpresult /h C:\temp\gp.html and check the Windows Defender branch: local Set-MpPreference values are overwritten at the next refresh, the most common reason exclusions “stop working” after a reboot.

Why not just turn it off

Disabling real-time protection is a legitimate troubleshooting step and a bad permanent decision. On a Windows host with a public IP, an exposed RDP endpoint or a web application that accepts uploads, the first malicious executable usually arrives through one of those three doors. With exclusions in place the scanner stops costing you throughput on the paths that matter and keeps costing you a few percent of CPU elsewhere.

The baseline that works: Defender on, the IIS and SQL exclusion set applied, a weekly full scan throttled to 25 percent, ASR rules audited then enabled, and signatures kept under 24 hours old. Take a performance snapshot before and after the change so any difference can be attributed to the exclusions rather than guessed at.

Leave a Comment