← Back to Notes

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.

N
NOTE

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.

N
NOTE

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

ComponentValue
Host OSWindows 11 25H2 (build 26200)
WSL2.7.13.0, kernel 6.18.33
DockerDocker Engine inside the WSL distro (not Docker Desktop)
Containerscomfyui (8188), open-webui (3000→8080), searxng (8080), ollama (11434)
Host Tailscale IP<tailscale-ip>
ClientLaptop with Tailscale installed and logged into the same tailnet

Why it didn't work out of the box

N
NOTE

"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) --> comfyui
Mirrored 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:8188 on the host
  • An Administrator PowerShell window for the firewall steps
W
WARN

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 --version

Expected (or newer):

WSL version: 2.7.13.0
Windows version: 10.0.26200.xxxx

Why: 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.

N
NOTE

"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 ls

Expected for this guide:

NAME        DESCRIPTION                               DOCKER ENDPOINT               ERROR
default *   Current DOCKER_HOST based configuration   unix:///var/run/docker.sock

Why: this decides which path to follow.

OutputMeaningWhat to do
default * → unix:///var/run/docker.sockDocker Engine installed inside WSLFollow every step
desktop-linux *Docker DesktopSkip 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\.wslconfig

Why: 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\.wslconfig and add networkingMode=mirrored under 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\.wslconfig

Expected output:

[wsl2]
networkingMode=mirrored

Why this method:

  • `n is PowerShell's newline character, so the file ends up with two separate lines.
  • -Encoding ascii writes 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.
W
WARN

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 --shutdown

Wait 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 eth2

Why: 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.

T
TIP

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 ollama

Step 2: Verify Docker and ComfyUI bind to all interfaces

Two layers must both accept outside connections:

  1. Docker's port mapping: which host interfaces the published port listens on
  2. 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>:8188

Expected results

docker ps: the port must be published on 0.0.0.0:

comfyui  0.0.0.0:8188->8188/tcp, [::]:8188->8188/tcp

ss: 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.3

Why 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.

!
BUG

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).

!
BUG

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 Allow

Why:

  • Windows Firewall blocks unsolicited inbound connections by default.
  • -RemoteAddress 100.64.0.0/10 limits 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.
W
WARN

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/10

Why:

  • 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.
N
NOTE

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>:8188

Expected: 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.

N
NOTE

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:

ServiceHost portRecommendation
Open WebUI3000Safe to expose on the tailnet (it has its own login)
SearXNG8080Fine if you want private search on the laptop
Ollama11434Only 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/10
T
TIP

Use 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.

#TestWhereIf it fails, the problem is...
1curl -I http://localhost:8188WSLComfyUI or the container isn't running
2curl -I http://<tailscale-ip>:8188WSLBind address (Step 2) or mirrored mode (Step 1)
3Test-NetConnection <tailscale-ip> -Port 8188Windows hostHyper-V firewall rule (Step 4)
4tailscale ping <pc-name>LaptopTailscale connectivity
5curl -I http://<tailscale-ip>:8188LaptopWindows Firewall rule (Step 3)
!
BUG

No 100.x address inside WSL — mirrored mode isn't active.

  • Check the file is exactly C:\Users\<you>\.wslconfig and not .wslconfig.txt: ls $env:USERPROFILE\.wslconfig*
  • Check the contents have [wsl2] on its own line
  • Run wsl --shutdown again and wait a full 10 seconds before reopening
  • Confirm wsl --version is 2.0.0 or newer
!
BUG

localhost:8188 stopped working on the host after enabling mirrored mode — add this to .wslconfig, then run wsl --shutdown:

[experimental]
hostAddressLoopback=true
!
BUG

Test-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, RemoteAddresses
!
BUG

Works 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 status on 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)
!
BUG

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
!
BUG

Containers are gone after a reboot or wsl --shutdown:

docker update --restart unless-stopped comfyui open-webui searxng ollama

Also 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).

!
BUG

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=8188

Keep 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:

  1. In the Tailscale admin console, enable MagicDNS and HTTPS Certificates under DNS.
  2. In an Administrator PowerShell on the host:
    tailscale serve --bg 8188
    tailscale serve status
  3. Open the printed https://<pc-name>.<tailnet>.ts.net URL on the laptop.
Mirrored + firewall (this guide)Tailscale Serve
URLhttp://<tailscale-ip>:8188https://<pc>.<tailnet>.ts.net
HTTPSNoYes, with a valid certificate
Firewall changesTwo rules per portNone
WSL config changeYesNo
Multiple servicesOne pair of rules eachOne Serve config per service/port
W
WARN

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 --shutdown

Quick 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>:8188
N
NOTE

Need 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 localhost is forwarded from Windows.
  • Mirrored mode: WSL shares the host's network interfaces, so services on 0.0.0.0 are reachable on every host IP, including Tailscale's.
  • 0.0.0.0 vs 127.0.0.1: 0.0.0.0 means "listen on every interface." 127.0.0.1 means "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}.