Windows Server Disk Space Management: Best Practices for WinSxS, Logs, and Temporary Files

A Windows Server that runs out of disk does not fail gracefully. Services stop, the event log stops writing, Windows Update fails with error 0x80070070, and backup jobs die quietly half-way through a VSS snapshot. On a VPS the root volume is usually 60–120 GB, so the usual Windows growth patterns — WinSxS from cumulative updates, pagefile sizing for 16 GB of RAM, and IIS or .NET logs nobody rotates — matter far more than they do on a physical server with spare bays. This is the cleanup order that reclaims space without breaking servicing, and the settings that stop it refilling.

Measure before deleting anything

Start with the free-space baseline, and record the top-level directories by size rather than opening Explorer and guessing. Get-ChildItem -Recurse on C:\Windows can take several minutes on a busy server; it is still cheaper than restoring a broken component store.

Before you start deleting, size the volume against realistic growth — the plan tiers on the Windows VPS plan comparison list the disk allocation you actually get, which is the number that decides whether 60 GB is enough.

Get-Volume | Where-Object DriveLetter -eq 'C' |
  Select-Object DriveLetter, FileSystemLabel, @{n='FreeGB';e={[math]::Round($_.SizeRemaining/1GB,1)}}, @{n='SizeGB';e={[math]::Round($_.Size/1GB,1)}}

Get-ChildItem C:\ -Directory -Force -ErrorAction SilentlyContinue |
  ForEach-Object {
    [pscustomobject]@{
      Path = $_.FullName
      GB   = [math]::Round((Get-ChildItem $_.FullName -Recurse -Force -File -ErrorAction SilentlyContinue |
              Measure-Object Length -Sum).Sum / 1GB, 2)
    }
  } | Sort-Object GB -Descending | Select-Object -First 12

WinSxS: use DISM, never Explorer

The only supported tool is DISM /Online /Cleanup-Image, and the order matters: analyze, clean, then optionally reset the base. Analysis reports how much is reclaimable before you commit to a change that removes the ability to uninstall recent updates; each pass takes 10–40 minutes on a shared CPU, so run it from a console session that survives a dropped RDP connection.

/ResetBase removes the superseded versions that allow uninstalling the most recent update. On a VPS that is usually the right trade-off — you want the space more than you want to roll back a cumulative update — but run it after a patch cycle has been validated, not in the middle of one. The PowerShell equivalent, Optimize-WindowsImage on a mounted WIM, does not apply to an online installation; do not mix the two up.

Cleanup targetTypical reclaim on a 100 GB VPSSafe to run onlineNotes
DISM StartComponentCleanup1.5–4 GBYesRemoves superseded components older than 30 days
DISM StartComponentCleanup /ResetBaseadds 0.5–2 GBYesBlocks uninstall of the last installed update
Windows Update Cleanup (cleanmgr)2–6 GBYesOverlaps with DISM; run one, then the other
Delivery Optimization cache1–3 GBYesClear-DeliveryOptimizationCache
CBS, DISM and MSI logs200–800 MBYesLogs grow without bound; rotate or delete
IIS logs (daily W3C)0.5–5 GBYesDepends entirely on traffic
Hibernation file40% of RAMYes on VPSDisable unless you need hybrid sleep
Pagefilefixed or system managedReboot neededNever disable on a low-RAM VPS
Expected reclaim on a 100 GB Windows Server VPS (Server 2022/2025, 8 GB RAM)

Windows Update cleanup and Delivery Optimization

Superseded update packages are the second-largest consumer after WinSxS. Disk Cleanup still works headlessly and is scriptable, which is why it survives on servers without the GUI role. Be careful with /sagerun on a production host, though: it also empties the Recycle Bin and clears temp directories for every profile it can reach, including an application pool identity. If an in-flight deployment stages files under C:\Windows\Temp, schedule the task outside deployment windows.

On a small VPS it is also worth capping the update cache rather than deleting it repeatedly. HKLM\SOFTWARE\Policies\Microsoft\Windows\DeliveryOptimization accepts MaxCacheSize as a percentage of disk; 5% of a 100 GB volume leaves about 5 GB of peer cache, enough for a single host to stop re-downloading the same cumulative update every month.

# Configure once interactively, then run unattended
cleanmgr /sageset:1        # tick Windows Update Cleanup, DoS files, logs, temp
cleanmgr /sagerun:1

# Delivery Optimization cache and its own cleanup
Get-DeliveryOptimizationStatus | Select-Object FileId, FileSizeInBytes
Clear-DeliveryOptimizationCache -Force

Pagefile and hibernation on a constrained VPS

The pagefile is not junk. Windows uses it for crash dumps, for commit charge beyond physical RAM, and for trimming working sets under memory pressure. On a VPS with 8 GB of RAM and a .NET workload, disabling it entirely is a reliable way to produce OutOfMemoryException under load even when Task Manager shows free memory.

What you can do is set a fixed size instead of letting it grow, so the file does not fragment and the disk reservation is predictable. A common rule for a VPS is 1.5× RAM up to 8 GB, then 1× beyond that, with a hard cap at 16 GB.

$cs = Get-WmiObject Win32_ComputerSystem
if ($cs.AutomaticManagedPagefile) { $cs.AutomaticManagedPagefile = $false; $cs.Put() }

$pf = Get-WmiObject Win32_PageFileSetting -Filter "Name='C:\\pagefile.sys'"
if (-not $pf) { $pf = (Get-WmiObject Win32_PageFileSetting).CreateInstance() }
$pf.Name = "C:\\pagefile.sys"
$pf.InitialSize = 8192
$pf.MaximumSize = 8192
$pf.Put()

# Hibernation: powercfg /h off frees hiberfil.sys
powercfg.exe /hibernate off

Log files that grow without a cap

Logs are the quietest consumer and the easiest win. A single IIS site writing 50 MB of W3C logs a day consumes 18 GB a year; SQL Server error logs, CBS logs, and .NET PerfMon logs all behave the same way. Set retention in the product where possible rather than relying on a script to delete files later.

The W3C format adds one file per site per day by default, and nothing in IIS enforces a total-size limit for the folder as a whole — truncateSize caps each individual log file, not the directory. On a server hosting a dozen small sites that distinction alone can mean 15 GB of logs where you expected 1 GB, so combine the configuration below with a folder-level retention job.

# IIS: cap total log volume per site, in bytes
Set-WebConfigurationProperty -PSPath 'IIS:\' `
  -Filter "system.applicationHost/sites/siteDefaults/logFile" `
  -Name truncateSize -Value 1073741824
Set-WebConfigurationProperty -PSPath 'IIS:\' `
  -Filter "system.applicationHost/sites/siteDefaults/logFile" `
  -Name periodicRestart.time -Value "00:00:00"

# Older than 30 days, from a scheduled job
Get-ChildItem C:\inetpub\logs\LogFiles -Recurse -Filter *.log |
  Where-Object LastWriteTime -lt (Get-Date).AddDays(-30) | Remove-Item -Force

If cleanup keeps coming back every month, the volume is undersized rather than badly managed — compare the disk allocations on the main Windows VPS hosting page before buying extra storage you will not fill.

Leave a Comment