Remote Access to ComfyUI (WSL2 + Docker) over Tailscale
ComfyUI runs in Docker inside WSL2 and works on localhost but not from other devices — even with Tailscale installed. This guide makes it reachable from any device on your tailnet while keeping it closed to the LAN and the public internet.
Summary. ComfyUI runs in a Docker container inside WSL2 on a Windows 11
desktop. It was reachable on the host at localhost:8188 but not from
other devices, even with Tailscale installed on both ends. This guide makes it
reachable from any device on the tailnet at http://<tailscale-ip>:8188, while
keeping it closed to the LAN and the public internet.
The fix, in one line: switch WSL2 to mirrored networking, confirm the
container binds to 0.0.0.0, and allow port 8188 through both the Windows
Firewall and the Hyper-V firewall, restricted to Tailscale's address range.
Throughout this guide, <tailscale-ip> is a placeholder for your host's own
Tailscale IP — an address in the 100.64.0.0/10 range. Find yours by running
tailscale ip -4 on the host, and substitute it wherever you see
<tailscale-ip>.
Environment
| Component | Value |
|---|---|
| Host OS | Windows 11 25H2 (build 26200) |
| WSL | 2.7.13.0, kernel 6.18.33 |
| Docker | Docker Engine inside the WSL distro (not Docker Desktop) |
| Containers | comfyui (8188), open-webui (3000→8080), searxng (8080), ollama (11434) |
| Host Tailscale IP | <tailscale-ip> |
| Client | Laptop with Tailscale installed and logged into the same tailnet |
Why it didn't work out of the box
"localhost works, Tailscale is installed — why can't the laptop connect?"
By default, WSL2 runs inside a lightweight VM behind its own virtual NAT. The
distro gets a private IP (e.g. 172.x.x.x) that the rest of the network can't
see.
To make life easier, WSL provides localhost forwarding: when something in WSL
listens on a port, Windows forwards 127.0.0.1:<port> on the host into the VM.
That's why localhost:8188 worked in the host's browser.
The catch: that forwarding is bound to the loopback interface only. When the
laptop connects to <tailscale-ip>:8188, the packet arrives on the host's
Tailscale interface, not loopback, and nothing is listening there. The
connection is refused or times out.
Default NAT mode
Laptop --"<tailscale-ip>:8188"--> Windows Tailscale iface --> x nothing listening (dropped)
Host browser --"localhost:8188"--> Windows loopback --> WSL forwarding --> WSL VM (172.x.x.x) --> comfyuiMirrored mode
Laptop --"<tailscale-ip>:8188"--> Windows Firewall --> Hyper-V Firewall --> WSL (shares host interfaces) --> comfyui (0.0.0.0:8188)With mirrored networking, WSL stops using its own NAT and instead shares the
host's network interfaces, including the Tailscale one. A service in WSL listening
on 0.0.0.0 then listens on the Tailscale IP directly. The only remaining
obstacles are the two firewalls.
Prerequisites
- Windows 11 22H2 or newer
- WSL 2.0.0 or newer
- Docker Engine running inside WSL (see Step 0)
- Tailscale installed and logged in on both the Windows host and the laptop
- ComfyUI already reachable at
http://localhost:8188on the host - An Administrator PowerShell window for the firewall steps
Confirm the baseline first. If localhost:8188 doesn't work on the host,
stop here. The problem is Docker or ComfyUI, not networking, and none of the
steps below will fix it.
Step 0: Check versions and Docker type
0.1 Check WSL and Windows versions
In PowerShell on the host:
wsl --versionExpected (or newer):
WSL version: 2.7.13.0
Windows version: 10.0.26200.xxxxWhy: mirrored networking needs WSL 2.0.0+ and Windows 11 22H2+ (build
22621+). Build 26200 is 25H2, which is fine. If WSL is older, run wsl --update.
"Windows version: 10.0..." — Windows 11 still reports itself as 10.0
internally. The build number is what matters.
0.2 Identify how Docker is installed
Inside the WSL distro:
docker context lsExpected for this guide:
NAME DESCRIPTION DOCKER ENDPOINT ERROR
default * Current DOCKER_HOST based configuration unix:///var/run/docker.sockWhy: this decides which path to follow.
| Output | Meaning | What to do |
|---|---|---|
default * → unix:///var/run/docker.sock | Docker Engine installed inside WSL | Follow every step |
desktop-linux * | Docker Desktop | Skip Steps 1 and 4. Docker Desktop already publishes ports on the Windows side, and mirrored mode is known to conflict with it. |
Step 1: Enable WSL mirrored networking
1.1 Check for an existing .wslconfig
Get-Content $env:USERPROFILE\.wslconfigWhy: if the file already exists (for example with memory= or processors=
limits for an AI workload), you want to add to it, not overwrite it.
- File exists: open it with
notepad $env:USERPROFILE\.wslconfigand addnetworkingMode=mirroredunder the existing[wsl2]section. - File doesn't exist (
Cannot find path...): create it as shown below.
1.2 Create the file
Set-Content -Path $env:USERPROFILE\.wslconfig -Value "[wsl2]`nnetworkingMode=mirrored" -Encoding ascii
Get-Content $env:USERPROFILE\.wslconfigExpected output:
[wsl2]
networkingMode=mirroredWhy this method:
`nis PowerShell's newline character, so the file ends up with two separate lines.-Encoding asciiwrites a plain file with no byte-order mark, so the WSL parser has nothing to choke on.- It avoids Notepad silently saving the file as
.wslconfig.txt, which WSL would ignore.
Wrong file = no effect. .wslconfig lives in your Windows user folder
(C:\Users\<you>\.wslconfig) and applies to all distros. It is not
/etc/wsl.conf inside the distro, which is a different file with different
settings.
1.3 Restart WSL
wsl --shutdownWait about 10 seconds, then open the distro again.
Why: .wslconfig is only read when the WSL VM starts. --shutdown stops the
VM completely, so the next launch picks up the new setting. This also stops your
containers.
1.4 Verify
Inside WSL:
ip addr | grep "inet 100."Expected (your own Tailscale IP will differ):
inet <tailscale-ip>/32 brd <tailscale-ip> scope global noprefixroute eth2Why: seeing the host's Tailscale IP inside WSL proves mirrored mode is
active. WSL now shares the host's network interfaces, and eth2 is the mirrored
copy of the Tailscale adapter.
Containers after a restart. wsl --shutdown stops every container. If they
don't come back on their own, start them with docker start comfyui. To make
that automatic:
docker update --restart unless-stopped comfyui open-webui searxng ollamaStep 2: Verify Docker and ComfyUI bind to all interfaces
Two layers must both accept outside connections:
- Docker's port mapping: which host interfaces the published port listens on
- ComfyUI itself: which interfaces the Python server binds to inside the container
Run all three checks inside WSL:
docker ps --format '{{.Names}} {{.Ports}}'
ss -tlnp | grep 8188
curl -I http://<tailscale-ip>:8188Expected results
docker ps: the port must be published on 0.0.0.0:
comfyui 0.0.0.0:8188->8188/tcp, [::]:8188->8188/tcpss: something must be listening on all interfaces:
LISTEN 0 4096 0.0.0.0:8188 0.0.0.0:*
LISTEN 0 4096 [::]:8188 [::]:*curl: ComfyUI answers on the Tailscale IP:
HTTP/1.1 200 OK
Server: Python/3.12 aiohttp/3.14.3Why test from inside WSL first: this curl hits the Tailscale IP without passing through either firewall. If it succeeds, the application side is correct, and any later failure has to be a firewall. Narrowing down one layer at a time makes problems quick to isolate.
If docker ps shows 127.0.0.1:8188->8188/tcp — the container was started
with -p 127.0.0.1:8188:8188, which locks it to loopback. Recreate it with -p 8188:8188 (or ports: - "8188:8188" in Compose).
If ComfyUI itself only listens on loopback — ComfyUI must be launched with
--listen 0.0.0.0. Check the container's command:
docker inspect comfyui --format '{{.Config.Cmd}} {{.Config.Entrypoint}}'Step 3: Allow the port through Windows Firewall
Open PowerShell as Administrator and run:
New-NetFirewallRule -DisplayName "ComfyUI (Tailscale only)" `
-Direction Inbound -Protocol TCP -LocalPort 8188 `
-RemoteAddress 100.64.0.0/10 -Action AllowWhy:
- Windows Firewall blocks unsolicited inbound connections by default.
-RemoteAddress 100.64.0.0/10limits the rule to the address block Tailscale assigns to devices (the CGNAT range). Devices on your tailnet can connect. A device on your home Wi-Fi, or on a hotel network if the PC were a laptop, cannot.- The backticks (
`) are PowerShell line continuations, so the command can span several lines.
ComfyUI has no authentication. Anyone who can reach port 8188 can run
workflows on your GPU, and can read or write files through custom nodes. Never
widen this rule to Any, and never expose it with Tailscale Funnel.
Step 4: Allow the port through the Hyper-V firewall
In the same Administrator PowerShell:
New-NetFirewallHyperVRule -Name "ComfyUI-Tailscale" -DisplayName "ComfyUI (Tailscale only)" `
-Direction Inbound -VMCreatorId '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' `
-Protocol TCP -LocalPorts 8188 -RemoteAddresses 100.64.0.0/10Why:
- In mirrored mode, WSL traffic passes through a second firewall, the Hyper-V firewall, which blocks inbound traffic to WSL by default.
{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}is the fixed ID Windows uses for WSL. The rule applies only to WSL, not to other Hyper-V VMs. (This GUID is the same on every Windows machine — it's a Windows constant, not something specific to your PC.)- Without this rule the packet gets past the Windows Firewall and is then silently dropped before it reaches WSL. This is the step people most often miss.
Docker Desktop users: skip this step. Without mirrored mode, the Hyper-V firewall isn't in the path.
Step 5: Test from the laptop
5.1 Confirm the tailnet path
On the laptop:
tailscale status
tailscale ping <pc-name>Why: tailscale status lists your devices and their names. tailscale ping
confirms the two machines can reach each other and shows whether the connection is
direct or relayed (DERP). If this fails, the problem is Tailscale itself, not the
steps above.
5.2 Test the port
curl -I http://<tailscale-ip>:8188Expected: HTTP/1.1 200 OK.
5.3 Open it in a browser
- By IP:
http://<tailscale-ip>:8188 - By name (MagicDNS enabled):
http://<pc-name>:8188
Why MagicDNS is nicer: Tailscale IPs are stable, but names are easier to remember and keep working if you ever re-register the machine.
Done. ComfyUI now loads on the laptop from anywhere with an internet connection, and it stays invisible to everything outside your tailnet.
Exposing other services (optional)
The rules above open only port 8188. To reach another container from the laptop, repeat Steps 3 and 4 with its host port:
| Service | Host port | Recommendation |
|---|---|---|
| Open WebUI | 3000 | Safe to expose on the tailnet (it has its own login) |
| SearXNG | 8080 | Fine if you want private search on the laptop |
| Ollama | 11434 | Only if needed, because its API has no authentication |
Example for Open WebUI:
New-NetFirewallRule -DisplayName "Open WebUI (Tailscale only)" `
-Direction Inbound -Protocol TCP -LocalPort 3000 `
-RemoteAddress 100.64.0.0/10 -Action Allow
New-NetFirewallHyperVRule -Name "OpenWebUI-Tailscale" -DisplayName "Open WebUI (Tailscale only)" `
-Direction Inbound -VMCreatorId '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' `
-Protocol TCP -LocalPorts 3000 -RemoteAddresses 100.64.0.0/10Use the host port, not the container port. Open WebUI is published as
3000->8080, so the firewall rule uses 3000, the port the host listens on.
The container's internal 8080 never touches the host network.
Troubleshooting
Work from the inside out. Each test adds one more layer, so the first test that fails tells you which layer is broken.
| # | Test | Where | If it fails, the problem is... |
|---|---|---|---|
| 1 | curl -I http://localhost:8188 | WSL | ComfyUI or the container isn't running |
| 2 | curl -I http://<tailscale-ip>:8188 | WSL | Bind address (Step 2) or mirrored mode (Step 1) |
| 3 | Test-NetConnection <tailscale-ip> -Port 8188 | Windows host | Hyper-V firewall rule (Step 4) |
| 4 | tailscale ping <pc-name> | Laptop | Tailscale connectivity |
| 5 | curl -I http://<tailscale-ip>:8188 | Laptop | Windows Firewall rule (Step 3) |
No 100.x address inside WSL — mirrored mode isn't active.
- Check the file is exactly
C:\Users\<you>\.wslconfigand not.wslconfig.txt:ls $env:USERPROFILE\.wslconfig* - Check the contents have
[wsl2]on its own line - Run
wsl --shutdownagain and wait a full 10 seconds before reopening - Confirm
wsl --versionis 2.0.0 or newer
localhost:8188 stopped working on the host after enabling mirrored mode —
add this to .wslconfig, then run wsl --shutdown:
[experimental]
hostAddressLoopback=trueTest-NetConnection fails on the host but the WSL curl succeeds — the
Hyper-V rule is missing or wrong. Check it:
Get-NetFirewallHyperVRule -Name "ComfyUI*" | Format-List Name, Direction, Action, LocalPorts, RemoteAddressesWorks on the host, times out from the laptop:
- Check the Windows Firewall rule exists and is enabled:
Get-NetFirewallRule -DisplayName "ComfyUI*" | Format-List DisplayName, Enabled, Action - Run
tailscale statuson both devices to confirm they're online and on the same tailnet - Check Tailscale ACLs in the admin console if you've customised them (the default allows everything)
The UI loads but is stuck on "Reconnecting…" — ComfyUI uses a websocket for live progress and queue updates. Something is blocking it:
- Turn Brave Shields (or similar ad blockers) off for that address
- Try a private window with extensions disabled
Containers are gone after a reboot or wsl --shutdown:
docker update --restart unless-stopped comfyui open-webui searxng ollamaAlso make sure the Docker service starts with the distro. If it doesn't, check
whether systemd is enabled in /etc/wsl.conf ([boot] → systemd=true).
Mirrored mode causes problems with other tools — fall back to NAT mode with
a Windows port proxy. Remove networkingMode=mirrored, run wsl --shutdown,
then in Admin PowerShell:
netsh interface portproxy add v4tov4 listenaddress=<tailscale-ip> listenport=8188 connectaddress=127.0.0.1 connectport=8188Keep the Step 3 rule and delete the Step 4 rule. Caveat: if the Tailscale service isn't up when Windows boots, the proxy may fail to bind until you re-add it.
Alternative: Tailscale Serve (no firewall changes)
If you'd rather not touch WSL networking or firewalls at all, Tailscale Serve
proxies tailnet traffic to the host's own localhost:
- In the Tailscale admin console, enable MagicDNS and HTTPS Certificates under DNS.
- In an Administrator PowerShell on the host:
tailscale serve --bg 8188 tailscale serve status - Open the printed
https://<pc-name>.<tailnet>.ts.netURL on the laptop.
| Mirrored + firewall (this guide) | Tailscale Serve | |
|---|---|---|
| URL | http://<tailscale-ip>:8188 | https://<pc>.<tailnet>.ts.net |
| HTTPS | No | Yes, with a valid certificate |
| Firewall changes | Two rules per port | None |
| WSL config change | Yes | No |
| Multiple services | One pair of rules each | One Serve config per service/port |
Serve, not Funnel. tailscale serve is tailnet-only. tailscale funnel
publishes to the public internet, so never use it for ComfyUI, Ollama or
anything else without authentication.
Rollback
To undo everything and return to the default setup:
# Remove firewall rules (Admin PowerShell)
Remove-NetFirewallRule -DisplayName "ComfyUI (Tailscale only)"
Remove-NetFirewallHyperVRule -Name "ComfyUI-Tailscale"
# Remove mirrored networking
Remove-Item $env:USERPROFILE\.wslconfig # or just delete the networkingMode line if the file has other settings
wsl --shutdownQuick reference
# --- Windows host (Admin PowerShell) ---
Set-Content -Path $env:USERPROFILE\.wslconfig -Value "[wsl2]`nnetworkingMode=mirrored" -Encoding ascii
wsl --shutdown
New-NetFirewallRule -DisplayName "ComfyUI (Tailscale only)" -Direction Inbound -Protocol TCP -LocalPort 8188 -RemoteAddress 100.64.0.0/10 -Action Allow
New-NetFirewallHyperVRule -Name "ComfyUI-Tailscale" -DisplayName "ComfyUI (Tailscale only)" -Direction Inbound -VMCreatorId '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' -Protocol TCP -LocalPorts 8188 -RemoteAddresses 100.64.0.0/10# --- Inside WSL: verify ---
ip addr | grep "inet 100."
docker ps --format '{{.Names}} {{.Ports}}'
ss -tlnp | grep 8188
curl -I http://<tailscale-ip>:8188
# --- Laptop: connect ---
tailscale ping <pc-name>
curl -I http://<tailscale-ip>:8188Need the full desktop, not just the web UI? This guide gets you to ComfyUI's web interface over your tailnet. When a task needs the actual Windows desktop instead — reading logs, running diagnostics, restarting a stuck service from your phone — see Remote Desktop over Tailscale, which sets up full RDP over the same encrypted tunnel.
Key concepts
- WSL2 NAT mode: the default. WSL sits behind a private virtual network, and
only
localhostis forwarded from Windows. - Mirrored mode: WSL shares the host's network interfaces, so services on
0.0.0.0are reachable on every host IP, including Tailscale's. 0.0.0.0vs127.0.0.1:0.0.0.0means "listen on every interface."127.0.0.1means "only this machine."100.64.0.0/10: the CGNAT range Tailscale uses for device IPs. Scoping firewall rules to it keeps services tailnet-only.- Hyper-V firewall: a separate firewall layer for WSL and other VMs,
identified by the WSL VMCreatorId
{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}.