Overview

In June 2026, a Docker Desktop update on SPECULAR-CORE, running alongside Proton VPN's always-on kill switch, brought down the entire local AI stack simultaneously. Open WebUI lost its data volume. Ollama stopped accepting connections from inside containers. ComfyUI refused to import its dependencies. The monitoring stack reported every service as down.

The immediate instinct was to patch each failure in isolation. The correct move turned out to be more fundamental: decommission Docker Desktop for Windows, install a native Linux Docker engine inside WSL2, and remove Docker Desktop's Windows and Hyper-V bridge from the recovered container-to-host path.

This case study documents what broke, why it broke, and the exact sequence of decisions that rebuilt the stack into something structurally more resilient than what existed before.

Snapshot Boundary

This article records the June 2026 recovery topology. It does not describe every service later added to SPECULAR-CORE, and it does not claim that native WSL2 Docker removes every possible VPN, kernel, bridge, or host-network failure. It records the narrower result proved here: the Docker Desktop and Windows Hyper-V path was removed from this stack.

Original Architecture

The stack before the failure ran across two distinct execution layers with Docker Desktop for Windows acting as the bridge between them.

Ollama and ComfyUI ran natively inside WSL2 Ubuntu, serving their APIs on localhost ports. Docker Desktop for Windows managed the frontend containers — Open WebUI, Uptime Kuma, and Portainer — using its own Hyper-V virtual network switches to route traffic between the Windows host, the Docker bridge network, and the WSL2 subsystem.

ServiceLayerPort
OllamaNative WSL211434
ComfyUINative Python venv8008
Open WebUIDocker Desktop container3000
Uptime KumaDocker Desktop container3001
PortainerDocker Desktop container9000

This worked until it didn't. The architecture required traffic to traverse three distinct network namespaces on every request: the Docker bridge, the Hyper-V virtual switch, and the WSL2 subsystem. Each crossing was a potential failure point.

Architecture Decision

The original architecture was functional but fragile. Three network namespace crossings per request means three places where a firewall policy, a kernel update, or a VPN routing change can silently break everything at once.

The Failure Chain

Two things happened within the same maintenance window. Docker Desktop updated itself, and Proton VPN's always-on kill switch was active.

The update changed how Docker Desktop managed its internal Hyper-V network switches. Proton VPN's kernel-level firewall, running in always-on mode, flagged the resulting cross-subsystem traffic as an unencrypted local data leak and dropped it. Traffic traversing from the Docker bridge through the Windows host OS to the WSL2 backend was systematically blocked.

The result was a cascade. Every container that needed to reach Ollama got ECONNREFUSED. Open WebUI's WebSocket connections dropped with xhr poll error. The volume mount that held the user database got swapped to an empty named volume during the Docker update, wiping the active session. Every monitor in Uptime Kuma flipped red simultaneously.

From the outside it looked like total system failure. From the inside, each service had a distinct root cause.

Phase I — Hyper-V Network Collapse

Phase IHyper-V Network Collapse

Every container that attempted to reach Ollama on host.docker.internal:11434 returned a connection refused error. Switching to 172.17.0.1, 172.23.x.x, and localhost all produced the same result. The Ollama service was confirmed running and listening on 0.0.0.0:11434 via ss -tlnp inside WSL2.

Root Cause

Proton VPN's kernel-level firewall was dropping all traffic crossing the Windows Hyper-V virtual switch between the Docker bridge network and the WSL2 network namespace. The packets left the container correctly but were intercepted before reaching the WSL2 subsystem. No Windows firewall rule, no Docker network flag, and no host.docker.internal alias could bypass a VPN kill switch operating at the kernel level.

Attempting to work around this with .wslconfig mirrored networking mode broke Docker Desktop's WSL2 integration entirely, producing Wsl/Service/0x8007274c errors and taking the Docker daemon offline. That was reverted immediately.

The structural fix was to remove Docker Desktop's Windows-managed bridge from this path. Installing a native Linux Docker engine inside WSL2 moved container networking onto the Linux host boundary; in the recovery tests, container-to-host traffic no longer crossed the Hyper-V hop that Proton VPN had blocked.

sudo apt-get update && sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --yes --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker

In the recovered configuration, 172.17.0.1 was the validated host-facing Docker bridge address for container-to-host traffic. It remained stable across the validation reboots without being presented as a universal or permanent address for every future Docker configuration.

Resolution

Native Linux Docker Engine installed inside WSL2. The recovered container-to-host path stays inside the Linux host boundary, and this build used 172.17.0.1 as its validated Docker bridge gateway instead of host.docker.internal.

Phase II — Volume Isolation and Data Loss

Phase IIVolume Isolation and Data Loss

After the networking fix, Open WebUI launched but presented a blank sign-up screen. All bots, knowledge bases, and user accounts were gone. The container was running but treating itself as a fresh installation.

Root Cause

The Docker Desktop update had swapped the volume mount from the explicit L drive path (/mnt/l/Openweb:/app/backend/data) to an anonymous named volume, silently isolating the container from the database file on the L drive. Additionally, migrating to native Linux Docker running as root caused an immediate permission collision with the existing ext4 data directory, causing the backend to crash in a silent restart loop rather than report the error clearly.

Two fixes were required. First, the file ownership on the data directory was realigned to the root Docker process context:

sudo chown -R root:root /home/atlas/openwebui-core-data
sudo chmod -R 775 /home/atlas/openwebui-core-data

Second, the container was redeployed with an explicit volume binding to the native Linux filesystem path rather than the NTFS-mounted L drive path. Open WebUI's database performs frequent write operations; on NTFS those writes produce locking conflicts under a root Docker daemon:

sudo docker run -d \
  -p 3000:8080 \
  -v /home/atlas/openwebui-core-data:/app/backend/data \
  -e OLLAMA_BASE_URL=http://172.17.0.1:11434 \
  --name open-webui \
  --restart always \
  ghcr.io/open-webui/open-webui:main

The user database was restored from the automated weekly backup at /mnt/l/Backups/openwebui-backup-20260601/webui.db. All bots, system prompts, knowledge bases, and RAG configurations were recovered intact.

Resolution

Open WebUI data directory migrated from NTFS L drive mount to native Linux filesystem at /home/atlas/openwebui-core-data. File ownership realigned to root Docker context. Database restored from weekly backup. All ten workbots recovered.

Phase III — ComfyUI venv Corruption

Phase IIIComfyUI venv Corruption

ComfyUI's Python environment, which had been created inside the ComfyUI directory on the NTFS-mounted L drive, stopped resolving installed packages. pip list reported packages as installed; python -c "import torch" returned ModuleNotFoundError. The venv was broken at the symlink level.

Root Cause

NTFS does not fully support the Linux symlink structure that Python virtual environments depend on. When the native Linux Docker daemon took over as root, the permission model for the L drive mount changed, invalidating the symlinks inside the venv. Packages were present on disk but the interpreter could no longer follow the symlink chain to find them.

The fix is an architectural principle rather than a patch: Python environments belong on the native Linux filesystem. Model files, code, and data can live on NTFS; the interpreter environment must not.

# Remove the broken venv from NTFS
rm -rf /mnt/l/StableDiffusion/ComfyUI/venv

# Create a new venv on the native Linux filesystem
python3.11 -m venv ~/comfyui-venv
source ~/comfyui-venv/bin/activate

# Reinstall dependencies using the absolute venv pip path
/home/atlas/comfyui-venv/bin/pip install torch torchvision torchaudio \
  --index-url https://download.pytorch.org/whl/cu128
/home/atlas/comfyui-venv/bin/pip install -r /mnt/l/StableDiffusion/ComfyUI/requirements.txt

The launch script was updated to call the venv Python by absolute path, bypassing any activation ambiguity:

#!/bin/bash
export PATH="/home/atlas/comfyui-venv/bin:$PATH"
cd /mnt/l/StableDiffusion/ComfyUI
/home/atlas/comfyui-venv/bin/python main.py \
  --listen 0.0.0.0 \
  --port 8008 \
  --output-directory /mnt/l/StableDiffusion/Output \
  --cuda-device 0 \
  --disable-cuda-malloc

All three models (Flux.1 Dev fp8, DreamShaper XL, Deliberate v6) remained untouched on the L drive throughout. No re-downloading was required.

Resolution

venv relocated from NTFS L drive to native Linux filesystem at ~/comfyui-venv. Launch script updated to use absolute Python path. All 25GB of models recovered without re-download.

Phase IV — Ollama Bridge Routing

Phase IVOllama Bridge Routing

With Open WebUI running under native Linux Docker, the Ollama connection URL needed to be updated. host.docker.internal no longer resolved correctly without Docker Desktop's DNS injection layer. localhost and 127.0.0.1 refer to the container's own loopback, not the host. The WSL2 eth0 IP (172.23.x.x) works but changes on every WSL2 restart.

Root Cause

host.docker.internal is a Docker Desktop for Windows feature. Native Linux Docker does not inject this hostname. Without it, containers have no built-in mechanism to resolve the host machine's address. The WSL2 subsystem's eth0 IP is dynamically assigned by the Hyper-V NAT stack and changes across restarts.

The solution used in this recovery was the native Linux Docker bridge gateway: 172.17.0.1. It is the host-facing interface of the docker0 bridge and, in the validated configuration, remained stable across the reboot tests while avoiding dependence on the changing WSL2 eth0 address.

The Ollama systemd override was updated to broadcast on all interfaces and point at the correct model storage path:

echo -e "[Service]\nEnvironment=\"OLLAMA_HOST=0.0.0.0\"\nEnvironment=\"OLLAMA_MODELS=/mnt/l/Ollama/models\"" \
  | sudo tee /etc/systemd/system/ollama.service.d/override.conf

sudo systemctl daemon-reload && sudo systemctl restart ollama

Open WebUI's Ollama connection URL was set to http://172.17.0.1:11434. Uptime Kuma monitor URLs were updated to http://172.17.0.1:[port] for each service.

Resolution

In the recovered configuration, container-to-host service references used 172.17.0.1, required no hostname resolution, and reached Ollama from each tested container on port 11434.

New Architecture

After the migration, all five services operated within one Linux host boundary. Docker still used separate container network namespaces behind its Linux bridge, but container-to-host traffic no longer crossed Docker Desktop's Hyper-V layer or the Windows host network path. The recovered routing was simpler and easier to inspect without claiming that Windows could never affect the machine in another way.

ServiceLayerLaunch MethodInternal TargetExternal Port
OllamaNative WSL2systemd0.0.0.0:11434localhost:11434
ComfyUINative Python venvlaunch_comfyui.sh0.0.0.0:8008localhost:8008
Open WebUINative Linux Dockerdocker runhttp://172.17.0.1:11434localhost:3000
PortainerNative Linux Dockerdocker run/var/run/docker.socklocalhost:9000
Uptime KumaNative Linux Dockerdocker run172.17.0.1:[port]localhost:3001

The Windows startup sequence (ATLAS_BOOTSTRAP.bat) was updated to start the native Linux Docker service via WSL rather than waiting for Docker Desktop:

wsl -d Ubuntu -u root bash -c "service docker start"

Docker Desktop for Windows is disabled on startup and no longer part of the boot sequence.

Maintenance Constraint

In the June 2026 recovery configuration, Docker Desktop remained disabled because running it beside the native engine reintroduced port and routing conflicts. Container-to-host lookups in this recorded topology used the validated Linux bridge address rather than a Docker Desktop hostname.

Outcomes

Architecture path
Docker Desktop and the Windows Hyper-V hop removed from container-to-host routing
Services recovered
all five — Open WebUI, Ollama, ComfyUI, Portainer, Uptime Kuma
Data recovered
ten workbots, all knowledge bases, full RAG configuration
Rebuild time
full stack operational from bare WSL2 in under 45 minutes

The immediate outcome was a working system. The more significant outcome was removal of the specific Windows-managed container path implicated in the collapse. That narrowed the failure surface for this topology, but it did not make the wider machine immune to every VPN, kernel, bridge, or Windows networking change.

The transferable principle is this: when a compound failure exposes a structural dependency you didn't know existed, the correct response is not to patch around it but to remove it. The Docker Desktop bridge between Windows and WSL2 was convenient until it became a single point of failure for everything. Replacing it with something simpler — a native Linux engine and a validated bridge path — cost an afternoon and removed that known failure path from the recovered stack.