# Requires: run as Administrator
# Purpose:
# - Docker in WSL listens ONLY on tcp://<WSL_IP>:2375 (not 0.0.0.0)
# - Windows exposes it as tcp://127.0.0.1:2375 via portproxy (no firewall inbound rule needed)
# - Ensures Docker is running in WSL before configuring portproxy
# - Sets DOCKER_HOST=tcp://localhost:2375 (User scope)
$ErrorActionPreference = "Stop"
# Change if your distro is not the default, e.g. "Ubuntu-22.04"
$distro = "Ubuntu"
$listenPort = 2375
function WslSh([string]$cmd) {
& wsl.exe -d $distro -- sh -lc $cmd
}
function WslRootSh([string]$cmd) {
& wsl.exe -d $distro -u root -- sh -lc $cmd
}
# 1) Get WSL IP (eth0)
$wslIp = (WslSh "ip -4 addr show eth0 | awk '/inet /{print \$2}' | cut -d/ -f1 | head -n1").Trim()
if (-not $wslIp -or $wslIp -notmatch '^\d{1,3}(\.\d{1,3}){3}$') {
throw "Could not determine WSL IP from eth0. Got: '$wslIp'"
}
# 2) Write daemon.json in WSL to restrict exposure to WSL IP only
# Docker will listen on:
# - unix:///var/run/docker.sock (normal Linux)
# - tcp://<WSL_IP>:2375 (restricted, not 0.0.0.0)
$daemonJson = @"
{
"hosts": ["unix:///var/run/docker.sock", "tcp://$wslIp:$listenPort"]
}
"@
# Use base64 to avoid quoting issues
$bytes = [System.Text.Encoding]::UTF8.GetBytes($daemonJson)
$b64 = [Convert]::ToBase64String($bytes)
WslRootSh "mkdir -p /etc/docker"
WslRootSh "echo '$b64' | base64 -d > /etc/docker/daemon.json"
# 3) Ensure Docker is running in WSL (start/restart as needed)
# Try systemd first (if enabled), else fallback to service
$hasSystemd = (WslSh "ps -p 1 -o comm= 2>/dev/null | tr -d '\r'").Trim() -eq "systemd"
if ($hasSystemd) {
WslRootSh "systemctl daemon-reload >/dev/null 2>&1 || true"
WslRootSh "systemctl restart docker >/dev/null 2>&1 || true"
} else {
WslRootSh "service docker restart >/dev/null 2>&1 || true"
}
# 4) Verify Docker responds in WSL (both unix and tcp)
# Unix socket check:
$okUnix = $true
try { WslSh "docker info >/dev/null 2>&1" | Out-Null } catch { $okUnix = $false }
if (-not $okUnix) {
throw "Docker does not seem to be running in WSL (docker info failed)."
}
# TCP listener check (dockerd listening on <WSL_IP>:2375):
$tcpListen = (WslSh "ss -lnt 2>/dev/null | awk '{print \$4}' | grep -E '(^|:)$listenPort$' | head -n1").Trim()
if (-not $tcpListen) {
throw "Docker is running, but not listening on tcp://$wslIp:$listenPort. Check /etc/docker/daemon.json and docker restart."
}
# 5) Configure Windows portproxy: 127.0.0.1:<port> -> <WSL_IP>:<port>
& netsh interface portproxy delete v4tov4 listenaddress=127.0.0.1 listenport=$listenPort | Out-Null
& netsh interface portproxy add v4tov4 listenaddress=127.0.0.1 listenport=$listenPort connectaddress=$wslIp connectport=$listenPort | Out-Null
# 6) Set DOCKER_HOST for current user (persistent)
[Environment]::SetEnvironmentVariable("DOCKER_HOST","tcp://localhost:$listenPort","User")
Write-Host "OK"
Write-Host " - WSL IP: $wslIp"
Write-Host " - Portproxy: 127.0.0.1:$listenPort -> $wslIp:$listenPort"
Write-Host " - DOCKER_HOST: tcp://localhost:$listenPort (User)"
Write-Host "Note: No firewall inbound rule was added (listenaddress=127.0.0.1 only)."