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 12WinSxS: 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 target | Typical reclaim on a 100 GB VPS | Safe to run online | Notes |
|---|---|---|---|
| DISM StartComponentCleanup | 1.5–4 GB | Yes | Removes superseded components older than 30 days |
| DISM StartComponentCleanup /ResetBase | adds 0.5–2 GB | Yes | Blocks uninstall of the last installed update |
| Windows Update Cleanup (cleanmgr) | 2–6 GB | Yes | Overlaps with DISM; run one, then the other |
| Delivery Optimization cache | 1–3 GB | Yes | Clear-DeliveryOptimizationCache |
| CBS, DISM and MSI logs | 200–800 MB | Yes | Logs grow without bound; rotate or delete |
| IIS logs (daily W3C) | 0.5–5 GB | Yes | Depends entirely on traffic |
| Hibernation file | 40% of RAM | Yes on VPS | Disable unless you need hybrid sleep |
| Pagefile | fixed or system managed | Reboot needed | Never disable on a low-RAM VPS |
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 -ForcePagefile 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 offLog 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.


