Docker on Windows can occasionally leave behind large amounts of container layer data under the Docker data directory, particularly in:
C:\ProgramData\Docker\windowsfilter
In some cases, normal Docker cleanup commands such as:
docker system prune -a
docker builder prune -a
docker image prune -a
may not recover all disk space consumed by Windows container layers.
This article provides a complete Docker local state reset script for Windows that stops the relevant Windows services, attempts to destroy Windows container layers through the Windows Host Compute Service storage API, removes the Docker data directory, recreates it, and starts the services again.
WARNING: THIS IS A DESTRUCTIVE FULL RESET OF LOCAL DOCKER STATE.
This script is intended only for experienced Windows administrators who have reviewed the script and fully understand the consequences of completely resetting Docker's local state.
Running this script can permanently and irrecoverably delete:
- All local Docker containers
- All local Docker images
- All Docker build cache
- All Docker volumes stored within the Docker data root
- All local container layers
- Docker network state stored within the Docker data root
- Other Docker state stored under the configured Docker data directory
There is no undo operation.
Do not run this script on a system containing Docker state that must be preserved.
This procedure is generally most appropriate for disposable development, CI/CD, build, or container-builder systems where local Docker state can safely be recreated.
When This May Be Useful
This procedure may be useful when a Windows Docker host has accumulated a large amount of disk usage that is not being reclaimed through normal Docker cleanup operations.
For example, the following directory may consume tens or hundreds of gigabytes:
C:\ProgramData\Docker\windowsfilter
while Docker itself reports considerably less reclaimable disk space.
Windows container layers are not ordinary directories. They are managed through the Windows container storage subsystem and Host Compute Service APIs. As a result, attempting to manually delete individual windowsfilter directories can result in access-denied errors, incomplete cleanup, or damaged Docker state.
The script below performs a complete local Docker state reset rather than attempting to selectively repair individual Docker layers.
Requirements
Run this procedure only if all of the following are true:
- You are a Windows administrator.
- You are running the script directly on the Windows Docker host.
- You understand that all local Docker state will be permanently deleted.
- You have reviewed the script before execution.
- Any important data stored in Docker volumes or containers has already been backed up.
- You have confirmed that Docker can safely be rebuilt from source images, registries, Dockerfiles, or other external sources.
- You agree to the Cloudmersive Terms of Service.
The script must be run from an elevated PowerShell session.
Save the Reset Script
Save the following script as:
Reset-DockerWindowsState.ps1
Review the entire script before executing it.
# Copyright Cloudmersive LLC 2026
#
# Reset-DockerWindowsState.ps1
#
# COMPLETE AND IRREVERSIBLE DOCKER STATE RESET FOR WINDOWS.
#
# IMPORTANT:
# USE OF THIS SCRIPT REQUIRES THAT YOU READ AND AGREE TO THE
# CLOUDMERSIVE TERMS OF SERVICE:
#
# https://portal.cloudmersive.com/terms-of-service
#
# IF YOU DO NOT AGREE TO THE CLOUDMERSIVE TERMS OF SERVICE,
# YOU MAY NOT USE THIS SCRIPT.
#
# This script permanently deletes local Docker state, including containers,
# images, layers, build cache, volumes stored in the Docker data root, and
# other Docker state.
#
# USE ONLY IF YOU ARE AN EXPERIENCED WINDOWS ADMINISTRATOR AND HAVE FULLY
# REVIEWED AND UNDERSTOOD THE CONSEQUENCES OF RUNNING THIS SCRIPT.
#
# THIS OPERATION CAN CAUSE IRREVERSIBLE DATA LOSS.
$ErrorActionPreference = "Continue"
$TermsUrl = "https://portal.cloudmersive.com/terms-of-service"
Write-Host ""
Write-Host "============================================================" -ForegroundColor Yellow
Write-Host " Cloudmersive - Docker for Windows Full State Reset" -ForegroundColor Yellow
Write-Host "============================================================" -ForegroundColor Yellow
Write-Host ""
Write-Host "Copyright Cloudmersive LLC 2026"
Write-Host ""
Write-Host "WARNING: THIS SCRIPT PERMANENTLY DELETES ALL LOCAL DOCKER STATE." -ForegroundColor Red
Write-Host ""
Write-Host "This includes, but is not limited to:"
Write-Host " - Containers"
Write-Host " - Images"
Write-Host " - Windows container layers"
Write-Host " - Build cache"
Write-Host " - Docker volumes stored inside the Docker data root"
Write-Host " - Docker network state"
Write-Host " - Other data stored inside the Docker data root"
Write-Host ""
Write-Host "THIS OPERATION CANNOT BE UNDONE." -ForegroundColor Red
Write-Host ""
Write-Host "Use of this script requires that you read and agree to the"
Write-Host "Cloudmersive Terms of Service:" -ForegroundColor Yellow
Write-Host $TermsUrl -ForegroundColor Cyan
Write-Host ""
Write-Host "If you do not agree to the Terms of Service, do not use this script." -ForegroundColor Yellow
Write-Host ""
# ----------
# Verify Administrator
# ----------
$CurrentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$Principal = New-Object Security.Principal.WindowsPrincipal($CurrentIdentity)
if (-not $Principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator))
{
Write-Host ""
Write-Host "ERROR: This script must be run from an elevated PowerShell session." -ForegroundColor Red
Write-Host "Right-click PowerShell and select 'Run as administrator'." -ForegroundColor Red
exit 1
}
# ----------
# Terms of Service acceptance
# ----------
Write-Host "By continuing, you confirm that you have read and agree to:" -ForegroundColor Yellow
Write-Host $TermsUrl -ForegroundColor Cyan
Write-Host ""
$TermsAcceptance = Read-Host "Type I AGREE to accept the Cloudmersive Terms of Service and continue"
if ($TermsAcceptance -cne "I AGREE")
{
Write-Host ""
Write-Host "Terms of Service were not accepted. No changes were made." -ForegroundColor Yellow
exit 1
}
# ----------
# Final destructive-operation confirmation
# ----------
Write-Host ""
Write-Host "FINAL WARNING" -ForegroundColor Red
Write-Host ""
Write-Host "You are about to permanently erase the local Docker state on this machine."
Write-Host "This operation may result in irreversible data loss."
Write-Host ""
$ResetConfirmation = Read-Host "Type DELETE ALL DOCKER STATE to continue"
if ($ResetConfirmation -cne "DELETE ALL DOCKER STATE")
{
Write-Host ""
Write-Host "Confirmation failed. No changes were made." -ForegroundColor Yellow
exit 1
}
# ----------
# Determine Docker data root
# ----------
$DockerRoot = $null
try
{
$DetectedDockerRoot = docker info --format '{{.DockerRootDir}}' 2>$null
if ($LASTEXITCODE -eq 0 -and $DetectedDockerRoot)
{
$DockerRoot = $DetectedDockerRoot.Trim()
}
}
catch
{
}
if (-not $DockerRoot)
{
$DockerRoot = "C:\ProgramData\Docker"
Write-Host ""
Write-Warning "Docker data root could not be detected automatically."
Write-Warning "Using default Windows Docker data root: $DockerRoot"
}
$DockerRoot = $DockerRoot.TrimEnd('\')
# Safety validation to avoid catastrophic deletion caused by an invalid path.
$ForbiddenRoots = @(
"C:",
"C:\",
"D:",
"D:\",
"E:",
"E:\",
"\"
)
if ($ForbiddenRoots -contains $DockerRoot)
{
Write-Host ""
Write-Host "ERROR: Refusing to operate on unsafe Docker root: $DockerRoot" -ForegroundColor Red
exit 1
}
$WindowsFilter = Join-Path $DockerRoot "windowsfilter"
Write-Host ""
Write-Host "Docker data root that will be permanently deleted:" -ForegroundColor Yellow
Write-Host " $DockerRoot" -ForegroundColor Yellow
Write-Host ""
$PathConfirmation = Read-Host "Type RESET to confirm this Docker data root"
if ($PathConfirmation -cne "RESET")
{
Write-Host ""
Write-Host "Docker data-root confirmation failed. No changes were made." -ForegroundColor Yellow
exit 1
}
# ----------
# Record free disk space before reset
# ----------
$DriveLetter = [System.IO.Path]::GetPathRoot($DockerRoot).TrimEnd('\')
$FreeBefore = $null
try
{
$FreeBefore = (Get-PSDrive -Name $DriveLetter.TrimEnd(':')).Free
}
catch
{
}
# ----------
# Stop Docker and Windows container compute services
# ----------
Write-Host ""
Write-Host "Stopping Docker services..." -ForegroundColor Cyan
Stop-Service docker -Force -ErrorAction SilentlyContinue
Write-Host "Stopping Windows Host Compute Service..." -ForegroundColor Cyan
Stop-Service vmcompute -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 3
# ----------
# Load Windows Compute Storage API
# ----------
Write-Host ""
Write-Host "Loading Windows container storage API..." -ForegroundColor Cyan
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public static class CloudmersiveHcsNative
{
[DllImport("ComputeStorage.dll",
CharSet = CharSet.Unicode,
SetLastError = true)]
public static extern int HcsDestroyLayer(string layerPath);
}
"@ -ErrorAction SilentlyContinue
# ----------
# Destroy Windows container layers through HCS
# ----------
if (Test-Path $WindowsFilter)
{
Write-Host ""
Write-Host "Enumerating Windows container layers..." -ForegroundColor Cyan
$Layers = @(Get-ChildItem $WindowsFilter -Directory -Force -ErrorAction SilentlyContinue)
Write-Host "Found $($Layers.Count) layer directories."
Write-Host ""
foreach ($Layer in $Layers)
{
Write-Host "Destroying layer: $($Layer.Name)"
try
{
$Result = [CloudmersiveHcsNative]::HcsDestroyLayer($Layer.FullName)
if ($Result -eq 0)
{
Write-Host " HCS destroy completed." -ForegroundColor Green
}
else
{
Write-Warning (" HcsDestroyLayer returned HRESULT 0x{0:X8}" -f ($Result -band 0xffffffff))
}
}
catch
{
Write-Warning " HCS layer destruction failed: $($_.Exception.Message)"
}
}
}
else
{
Write-Host ""
Write-Host "No windowsfilter directory found at:" -ForegroundColor Yellow
Write-Host " $WindowsFilter"
}
# ----------
# Delete the remaining Docker state
# ----------
Write-Host ""
Write-Host "Deleting remaining Docker state..." -ForegroundColor Cyan
Write-Host "This step can take a considerable amount of time on systems containing"
Write-Host "large Windows container layer stores."
Write-Host ""
if (Test-Path $DockerRoot)
{
cmd.exe /c "rd /s /q `"$DockerRoot`""
}
# ----------
# Verify removal
# ----------
if (Test-Path $DockerRoot)
{
Write-Host ""
Write-Warning "Some Docker data remains under:"
Write-Warning $DockerRoot
Write-Warning ""
Write-Warning "The system may require further investigation or a reboot before"
Write-Warning "remaining Windows container storage objects can be removed."
}
else
{
Write-Host ""
Write-Host "Docker data root successfully removed." -ForegroundColor Green
}
# ----------
# Recreate Docker data directory
# ----------
if (-not (Test-Path $DockerRoot))
{
Write-Host ""
Write-Host "Creating clean Docker data root..." -ForegroundColor Cyan
New-Item -ItemType Directory -Path $DockerRoot -Force | Out-Null
}
# ----------
# Restart Windows container services
# ----------
Write-Host ""
Write-Host "Starting Windows Host Compute Service..." -ForegroundColor Cyan
Start-Service vmcompute -ErrorAction SilentlyContinue
Write-Host "Starting Docker..." -ForegroundColor Cyan
Start-Service docker -ErrorAction SilentlyContinue
Start-Sleep -Seconds 3
# ----------
# Report service state
# ----------
Write-Host ""
Write-Host "Service status:" -ForegroundColor Cyan
Get-Service docker, vmcompute -ErrorAction SilentlyContinue |
Select-Object Name, Status |
Format-Table -AutoSize
# ----------
# Report new Docker data-root size
# ----------
Write-Host ""
Write-Host "Current Docker data-root size:" -ForegroundColor Cyan
try
{
$RemainingBytes = (
Get-ChildItem $DockerRoot -Recurse -Force -File -ErrorAction SilentlyContinue |
Measure-Object Length -Sum
).Sum
if (-not $RemainingBytes)
{
$RemainingBytes = 0
}
Write-Host ("{0:N2} GB" -f ($RemainingBytes / 1GB))
}
catch
{
Write-Warning "Unable to calculate Docker data-root size."
}
# ----------
# Report approximate disk space recovered
# ----------
if ($null -ne $FreeBefore)
{
try
{
$FreeAfter = (Get-PSDrive -Name $DriveLetter.TrimEnd(':')).Free
$Recovered = $FreeAfter - $FreeBefore
Write-Host ""
Write-Host "Approximate disk space recovered:" -ForegroundColor Cyan
Write-Host ("{0:N2} GB" -f ($Recovered / 1GB))
}
catch
{
}
}
Write-Host ""
Write-Host "============================================================" -ForegroundColor Green
Write-Host " Docker state reset completed." -ForegroundColor Green
Write-Host "============================================================" -ForegroundColor Green
Write-Host ""
Write-Host "All previous local Docker state should be considered permanently deleted."
Write-Host "Required images must be pulled or rebuilt again."
Write-Host ""
Run the Script
Open PowerShell as Administrator, change to the directory containing the script, and run:
Set-ExecutionPolicy -Scope Process Bypass
.\Reset-DockerWindowsState.ps1
The script requires several explicit confirmations before performing any destructive operation.
First, the administrator must read and agree to the Cloudmersive Terms of Service:
https://portal.cloudmersive.com/terms-of-service
The administrator must then separately acknowledge that all Docker state will be deleted and confirm the Docker data directory that will be reset.
What the Script Does
The script performs the following operations:
- Verifies that PowerShell is running with Administrator privileges.
- Requires explicit acceptance of the Cloudmersive Terms of Service.
- Requires explicit confirmation of the irreversible Docker state reset.
- Attempts to determine the current Docker data root.
- Stops the Docker service.
- Stops the Windows Host Compute Service (
vmcompute).
- Enumerates Windows container layers under
windowsfilter.
- Calls the Windows
HcsDestroyLayer API for each layer.
- Deletes the remaining Docker data root.
- Creates a new empty Docker data directory.
- Restarts the Windows Host Compute Service.
- Restarts Docker.
- Reports the resulting service status and approximate disk space recovered.
Why windowsfilter Can Be Difficult to Delete
Windows container storage differs substantially from ordinary filesystem directories.
Windows container layers are managed through the Windows container storage and Host Compute Service infrastructure. Layer directories may contain state associated with container filesystem filters, reparse points, copy-on-write behavior, and Windows container layer metadata.
As a result, attempting to recover disk space by manually deleting directories such as:
C:\ProgramData\Docker\windowsfilter\<layer>
may fail with errors such as:
Access is denied.
It can also leave Docker or the Windows container storage subsystem in an inconsistent state.
This script therefore attempts to destroy each Windows container layer through the Windows HcsDestroyLayer API before deleting the remaining Docker data directory.
This Is a State Reset, Not Routine Maintenance
This procedure should not be used as routine Docker maintenance.
For ordinary Docker cleanup, administrators should first use the normal Docker lifecycle and cleanup mechanisms appropriate for their environment.
This full-reset procedure is intended for situations where:
- Docker local state can safely be discarded.
- Normal Docker cleanup has already proven insufficient.
- A Windows container build or development system has accumulated substantial unwanted disk usage.
- The administrator is prepared to recreate all required local Docker state.
Production systems containing persistent Docker volumes, unique local images, or irreplaceable container data should not be reset using this procedure unless appropriate backups and recovery procedures have been validated.
Final Warning
Running Reset-DockerWindowsState.ps1 deletes and recreates Docker's local data store.
All Docker state within the selected Docker data root should be considered permanently and irrecoverably deleted.
Only experienced administrators who have reviewed the source code and fully understand the impact of a complete Docker state reset should execute the script.
Use of the script requires agreement to the Cloudmersive Terms of Service:
https://portal.cloudmersive.com/terms-of-service