Windows 11 does some maintenance automatically, but its built-in tasks are conservative and unpredictable. They run in the background at random times, sometimes during a gaming session or an important call. Building your own maintenance automation with Task Scheduler and PowerShell gives you control over what runs, when it runs, and what it cleans — without relying on whatever Windows decides to do.
This guide covers building a complete maintenance routine: temp file cleanup, disk health checks, SSD optimization, Windows Update cleanup, and event log archiving — all automated on your schedule.
What Windows Maintenance Actually Needs
A well-maintained Windows 11 system benefits from:
- Weekly temp file cleanup — Windows and apps accumulate GBs of temporary files
- Monthly Windows Update cleanup — Old update packages can consume 5–15+ GB in
C:\Windows\SoftwareDistribution - SSD TRIM scheduling (usually automatic, but worth verifying)
- Disk health checks — S.M.A.R.T. checks via tools or CHKDSK
- Event log archiving — Prevent event logs from growing unbounded
- Prefetch/cache cleanup — Occasionally beneficial on low-storage systems
Building the Maintenance Script
Create a PowerShell script that handles all cleanup tasks. Save it to a stable path — C:\MaintenanceScripts\ is a clean choice.
# Create the scripts directory
New-Item -ItemType Directory -Path "C:\MaintenanceScripts" -Force
Now create the main maintenance script. Open Notepad or VS Code and save the following as C:\MaintenanceScripts\weekly-cleanup.ps1:
# weekly-cleanup.ps1
# Windows 11 Weekly Maintenance Script
# Run as Administrator
$logPath = "C:\MaintenanceScripts\Logs"
New-Item -ItemType Directory -Path $logPath -Force | Out-Null
$logFile = "$logPath\maintenance-$(Get-Date -Format 'yyyy-MM-dd').log"
function Write-Log {
param([string]$Message)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"$timestamp - $Message" | Tee-Object -FilePath $logFile -Append
}
Write-Log "=== Weekly Maintenance Started ==="
# --- 1. Clean Windows Temp Folder ---
Write-Log "Cleaning Windows Temp folder..."
$tempPath = $env:TEMP
$before = (Get-ChildItem $tempPath -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum
Get-ChildItem $tempPath -Recurse -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
$after = (Get-ChildItem $tempPath -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum
Write-Log "Temp folder cleaned. Freed approx $([math]::Round(($before - $after)/1MB, 2)) MB"
# --- 2. Clean System Temp Folder ---
Write-Log "Cleaning System Temp folder..."
Get-ChildItem "C:\Windows\Temp" -Recurse -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
Write-Log "System Temp folder cleaned."
# --- 3. Run Disk Cleanup (cleanmgr) silently ---
Write-Log "Running Disk Cleanup..."
# Set up cleanmgr to run with all flags enabled (sageset:100 configures preset 100)
$cleanmgrReg = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches"
$categories = @(
"Active Setup Temp Folders", "BranchCache", "Content Indexer Cleaner",
"D3D Shader Cache", "Delivery Optimization Files", "Device Driver Packages",
"Diagnostic Data Viewer database files", "Downloaded Program Files",
"Internet Cache Files", "Recycle Bin", "Service Pack Cleanup",
"Setup Log Files", "System error memory dump files",
"System error minidump files", "Temporary Files", "Thumbnail Cache",
"Update Cleanup", "Upgrade Discarded Files", "Windows Error Reporting Files"
)
foreach ($category in $categories) {
$regPath = "$cleanmgrReg\$category"
if (Test-Path $regPath) {
Set-ItemProperty -Path $regPath -Name "StateFlags0100" -Value 2 -Type DWord -ErrorAction SilentlyContinue
}
}
Start-Process cleanmgr -ArgumentList "/sagerun:100" -Wait -WindowStyle Hidden
Write-Log "Disk Cleanup completed."
# --- 4. Clear DNS Cache ---
Write-Log "Flushing DNS cache..."
Clear-DnsClientCache
Write-Log "DNS cache flushed."
# --- 5. Flush Windows Store Cache ---
Write-Log "Clearing Windows Store cache..."
Start-Process wsreset.exe -Wait -WindowStyle Hidden
Write-Log "Windows Store cache cleared."
# --- 6. Archive and Clear Event Logs older than 30 days ---
Write-Log "Archiving old event logs..."
$archivePath = "C:\MaintenanceScripts\EventLogArchives"
New-Item -ItemType Directory -Path $archivePath -Force | Out-Null
$logs = @("Application", "System", "Security")
foreach ($log in $logs) {
$archiveFile = "$archivePath\$log-$(Get-Date -Format 'yyyy-MM-dd').evtx"
try {
wevtutil epl $log $archiveFile
wevtutil cl $log
Write-Log "Archived and cleared $log event log."
} catch {
Write-Log "Warning: Could not process $log log: $_"
}
}
Write-Log "=== Weekly Maintenance Completed ==="
This script logs everything it does, so you have a record of each run.
Registering the Task in Task Scheduler
Now create a scheduled task that runs this script every Sunday at 3:00 AM:
# Register the weekly maintenance task (run in elevated PowerShell)
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File C:\MaintenanceScripts\weekly-cleanup.ps1"
$trigger = New-ScheduledTaskTrigger `
-Weekly `
-DaysOfWeek Sunday `
-At "03:00"
$settings = New-ScheduledTaskSettingsSet `
-ExecutionTimeLimit (New-TimeSpan -Hours 1) `
-StartWhenAvailable `
-RunOnlyIfNetworkAvailable:$false `
-WakeToRun:$false
$principal = New-ScheduledTaskPrincipal `
-UserId "SYSTEM" `
-LogonType ServiceAccount `
-RunLevel Highest
Register-ScheduledTask `
-TaskName "WeeklyWindowsMaintenance" `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-Principal $principal `
-Description "Weekly temp file cleanup, disk cleanup, and log archiving"
Running as SYSTEM with -RunLevel Highest ensures the script has the permissions needed to clean system folders and clear event logs.
Monthly Deep Cleanup Task
Create a second script for monthly tasks — things that are heavier or less frequently needed:
# monthly-cleanup.ps1
# Run as SYSTEM / Administrator
$logPath = "C:\MaintenanceScripts\Logs"
$logFile = "$logPath\monthly-$(Get-Date -Format 'yyyy-MM').log"
# --- 1. Run DISM Component Store Cleanup ---
Write-Output "$(Get-Date) - Running DISM component cleanup..." | Tee-Object $logFile -Append
DISM /Online /Cleanup-Image /StartComponentCleanup /ResetBase
# --- 2. Run SFC Scan ---
Write-Output "$(Get-Date) - Running SFC scan..." | Tee-Object $logFile -Append
sfc /scannow
# --- 3. Run Optimize-Volume (TRIM for SSDs, defrag for HDDs) ---
Write-Output "$(Get-Date) - Running volume optimization..." | Tee-Object $logFile -Append
Get-Volume | Where-Object {$_.DriveType -eq "Fixed" -and $_.DriveLetter} | ForEach-Object {
Write-Output "Optimizing drive $($_.DriveLetter)..."
Optimize-Volume -DriveLetter $_.DriveLetter -Verbose
}
# --- 4. Check disk health via SMART ---
Write-Output "$(Get-Date) - Checking disk health..." | Tee-Object $logFile -Append
Get-PhysicalDisk | Select-Object FriendlyName, MediaType, HealthStatus, OperationalStatus | Format-Table | Out-File $logFile -Append
Write-Output "$(Get-Date) - Monthly maintenance complete." | Tee-Object $logFile -Append
Register it as a monthly task:
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File C:\MaintenanceScripts\monthly-cleanup.ps1"
$trigger = New-ScheduledTaskTrigger -Monthly -DaysOfMonth 1 -At "04:00"
Register-ScheduledTask `
-TaskName "MonthlyWindowsMaintenance" `
-Action $action `
-Trigger $trigger `
-Principal (New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest) `
-Settings (New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Hours 2) -StartWhenAvailable)
Managing Tasks via Task Scheduler GUI
To view, edit, or manually trigger your tasks:
- Press
Win+R, typetaskschd.msc, press Enter. - In the left panel, navigate to Task Scheduler Library.
- Find WeeklyWindowsMaintenance and MonthlyWindowsMaintenance.
- Right-click → Run to execute immediately for testing.
- Check the History tab after a run to see the result codes (0 = success).
Viewing Maintenance Logs
Your scripts log to C:\MaintenanceScripts\Logs\. Open any log file in Notepad or PowerShell:
Get-Content "C:\MaintenanceScripts\Logs\maintenance-2026-04-19.log"
Disabling Windows’ Own Automatic Maintenance
Windows has a built-in Automatic Maintenance task that runs at 2:00 AM. If you’re managing your own schedule, you may want to adjust it to avoid conflicts:
- Open Control Panel → Security and Maintenance → Maintenance.
- Click Change maintenance settings.
- Set it to run at a different time, or uncheck Allow scheduled maintenance to wake up my computer.
Your custom Task Scheduler tasks will coexist cleanly with Windows’ built-in tasks — they don’t conflict, just overlap if scheduled at the same time.
Taking 30 minutes to build this automation means your system stays clean without you thinking about it. The logs give you a history of what was cleaned, and the -StartWhenAvailable flag ensures tasks run even if the PC was asleep at the scheduled time.