Skip to content

[Windows] Docker VMM: com.docker.sailor.exe holds ~15 GB host memory with a 4 GiB VM and does not return it under sustained memory pressure #724

Open

Description

@CoolZxp

Bug description

With Docker Desktop 4.93.0 (240920) on Windows using the Docker VMM backend (engine linux/sailor), the VMM process com.docker.sailor.exe holds ~15 GB of host working set, while the Docker VM is configured with only 4 GiB of RAM and the containers inside use ~1.5 GiB.

The memory is never returned to the host, even though the VMM itself detects sustained host memory pressure and continuously tries to reclaim. Its reclaim action only signals the guest to drop page caches, which frees almost nothing (the guest already has ~1.9 GiB free and only ~0.8 GiB of page cache). Only restarting Docker Desktop releases the memory; it then grows back to ~15 GB over several hours of normal use.

The docs state Docker VMM "Returns idle memory to the host when containers aren't active" (https://docs.docker.com/desktop/features/vmm/), so this looks like a bug in the host-side memory reclaim on Windows.

Snapshot (2026-10-04 11:18 local; VMM process started 2026-10-04 01:32)

com.docker.sailor.exe:

  • Working set: 14,541 MB (observed range over the day: 14,379–16,919 MB)
  • Working set - private: 3,079 MB
  • Private bytes (commit): 4,454 MB
  • The remainder (~11.5 GB) is shareable working set and is not guest RAM: the VM reports 4,167,532 kB total with MemFree ~1.9 GB and Cached ~0.8 GB.

Host (32 GB RAM, NVMe SSD):

  • Available memory: ~358 MB
  • Commit: 51.3 / 63.8 GB
  • Pagefile usage: ~5.9 GB
  • Sustained hard page faults: ~2,170 pages/s (\Memory\Pages Input/sec), while disks are otherwise idle (6.6% disk time)

VM / containers:

  • VM: 4 GiB RAM (MemoryMiB=4096), 16 vCPU
  • clickhouse/clickhouse-server: ~1.0 GiB
  • postgres:17-alpine: ~0.5 GiB
  • Containers are idle most of the time

Evidence from Docker's own host log

%LOCALAPPDATA%\Docker\log\host\monitor.log* contains 3,676 lines between 2026-10-03 16:07 and 2026-10-04 11:18 local time (roughly one every ~10 s while pressure is active, alternating info/warning):

[2026-10-03T08:07:02.632509100Z] {"component":"sailor","level":"warning","msg":"host memory pressure: signalled guest drop-caches","target":"drop_caches_host","time":"2026-10-03T16:07:02.632509100+08:00"}
[2026-10-04T03:18:21.429501900Z] {"component":"sailor","level":"info","msg":"host memory pressure: signalled guest drop-caches","target":"drop_caches_host","time":"2026-10-04T11:18:21.428746200+08:00"}

The VMM sees the pressure and keeps reclaiming, but the reclaim only reaches guest page cache while ~11.5 GB of host-side shareable working set is left untouched.

Impact

Host available memory drops to a few hundred MB and the machine hard-faults continuously, noticeably slowing down daily work. docker desktop restart fixes it temporarily. There is no user-facing setting to bound this host-side memory.

Reproduction steps

  1. Windows 10 x64 (build 26300), Docker Desktop 4.93.0, VMM backend selected (Settings > General > Virtual Machine Manager), VM memory at the 4 GiB minimum, 16 CPUs.
  2. Start a couple of long-running containers (e.g. ClickHouse + Postgres) and use Docker normally for a few hours, on a host under moderate memory pressure (IDEs + browser using most of 32 GB).
  3. Watch com.docker.sailor.exe in Task Manager (Details) and %LOCALAPPDATA%\Docker\log\host\monitor.log.
  4. The process working set grows to ~15 GB (≈4x the configured VM RAM) and does not shrink despite sustained host memory pressure; the log fills with "host memory pressure: signalled guest drop-caches".
  5. docker desktop restart releases the memory; it regrows over hours.

Platform

Windows

Version information

docker version:
Client:
 Version:           29.8.1
 API version:       1.56
 Go version:        go1.26.8
 Git commit:        4a63305
 Built:             Tue Sep 15 16:28:39 2026
 OS/Arch:           windows/amd64
 Context:           desktop-linux

Server: Docker Desktop 4.93.0 (240920)
 Engine:
  Version:          29.8.1
  API version:      1.56 (minimum version 1.40)
  Go version:       go1.26.8
  Git commit:       464cd50
  Built:            Tue Sep 15 16:25:38 2026
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v2.3.5
 runc:
  Version:          v1.5.1

docker info (server, trimmed):
 Server Version: 29.8.1
 Storage Driver: overlayfs
  driver-type: io.containerd.snapshotter.v1
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 2
 Kernel Version: 7.0.14-linuxkit   [engine: linux/sailor — Docker VMM Beta]
 Operating System: Docker Desktop
 OSType: linux
 Architecture: x86_64
 CPUs: 16
 Total Memory: 3.879GiB
 Name: docker-desktop
 Docker Root Dir: /var/lib/docker

Host:
 Windows 10 Pro for Workstations, version 26H2, build 26300.9550
 32 GB RAM, NVMe SSD, Windows Hypervisor Platform enabled

Activity

  1. CoolZxp commented on Oct 4, 2026

    @CoolZxp
    Author

    Dug into this a bit more, since I wanted to know where the memory actually is instead of guessing. I broke down com.docker.sailor.exe's working set with VirtualQueryEx/QueryWorkingSetEx (the same data VMMap shows). This is 4.93.0, Windows 10 26H2 (26300), VM at 4 GiB:

    Region Size Type Resident Locked
    VHDX mapping 102,028 MB (99.64 GiB) MEM_MAPPED, PAGE_READWRITE 10,977 MB 0
    Guest RAM 4,096 MB MEM_PRIVATE, RW 3,032 MB 0
    One small mapped file 744 MB MEM_MAPPED, read-only 56 MB 0
    Everything else (615 regions) — IMAGE / PRIVATE small 0

    Working set at that moment: 14,272 MB, of which 3,103 MB private.

    The 102,028 MB region matches %LOCALAPPDATA%\Docker\vm-data\DockerDesktop.vhdx exactly, so the whole disk image is mapped into the host process (which also explains its ~110 GB virtual size). ~11 GB of it is resident and file-backed — that's where the memory goes. Not guest RAM: guest RAM is the separate 4 GiB private region with ~3 GB resident.

    None of it is locked, so in theory it can be trimmed, but it stayed resident for ~19 h while the host sat at 0.3–0.8 GB available (standby under 1 GB), and monitor.log kept logging "host memory pressure: signalled guest drop-caches" every ~10 s the whole time.

    To make sure it wasn't somehow pinned, I called EmptyWorkingSet on the process once (VM left running):

    before +0.5 s +60 s ~5 min
    VMM working set 12,906 MB 159 MB 922 MB 1,865 MB
    Host available 2,513 MB 12,966 MB 12,405 MB 11,531 MB
    Standby list 1,683 MB 12,552 MB 12,390 MB 11,500 MB

    12.7 GB came out of the working set in 1.76 s, most of it landing on the standby list (standby grew from 1.7 GB to 12.6 GB). Only ~1.9 GB was back after 5 minutes, so most of it was cold cache that nothing needed. The VM was completely fine — both containers stayed up and Postgres/ClickHouse answered normally. The pressure messages stopped right after, too.

    So the pages are clean, unlocked and reclaimable, and trimming them is transparent to the VM. It just doesn't happen on its own: Windows' regular pressure trimming didn't free it, and the VMM's reclaim path only pokes the guest caches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions