If you run both Tailscale and Docker on a Linux system, you may encounter intermittent network drop-outs, failing Docker pulls, or container connection timeouts. A particularly painful manifestation of this issue is when a Pi-hole container (acting as your local DNS server) loses upstream internet routing, causing DNS resolution to fail for your entire local network.
This guide explains why this happens, how to diagnose it, and how to fix it permanently.
- Outbound container connection failures: Standard Docker pulls or inside-container web requests fail with:
connect: no route to host - DNS Outages (if running Pi-hole/AdGuard in Docker): Devices on your local network intermittently lose internet connection because the DNS container cannot forward queries to upstream WAN servers (like
1.1.1.1or8.8.8.8). - Host vs. Daemon discrepancy: The host machine can ping or curl websites normally (often over IPv6 or via direct host sockets), but the Docker daemon or containers fail to connect to the exact same IP addresses.
- Connection starts, then drops: Large downloads (like pulling multiple Docker layers in parallel) work initially, then suddenly fail mid-pull.
To isolate the issue, run the following diagnostic commands on your Linux host:
Tailscale injects policy routing rules into the Linux kernel. Check your policy rules:
ip ruleIf you see rules referencing the 0x80000 fwmark with unreachable targets, Tailscale's ruleset is active:
5210: from all fwmark 0x80000/0xff0000 lookup main
5230: from all fwmark 0x80000/0xff0000 lookup default
5250: from all fwmark 0x80000/0xff0000 unreachable
Compare a UDP route trace with a TCP connection attempt to the target IP (e.g. ghcr.io or 8.8.8.8):
-
Run a UDP-based traceroute to see if the network route is physically open:
tracepath -n 4.208.26.196
(If the hops successfully go past your local router and out to the ISP/target network, the physical connection is working.)
-
Attempt a TCP connection on port 443 (HTTPS) to the same target:
curl -Iv --connect-timeout 5 https://4.208.26.196
If
curlfails instantly withNo route to hostdespitetracepathshowing an open path, the block is being enforced locally by policy routing or firewall rules.
Tailscale utilizes firewall marks (fwmark 0x80000) to direct VPN traffic and prevent routing loops.
When a Docker container (or the daemon pulling an image) initiates outbound traffic, packets pass through Docker's virtual bridge interface (docker0 or custom bridges) and are translated via NAT (Network Address Translation).
If Tailscale's stateful connection filtering is enabled on the host, it tracks and inspects these packets. The combination of Tailscale's firewall state tracking and Docker's NAT redirection can cause the Linux kernel to mistakenly apply the Tailscale policy routing rules to local container traffic, marking the packets as unreachable and dropping them.
To verify if Tailscale is indeed causing the block, stop the Tailscale daemon entirely:
sudo systemctl stop tailscaledTry pulling your Docker images or forwarding DNS queries again. If the issue immediately disappears, Tailscale's routing table is the source of the conflict.
Start Tailscale back up and instruct it to ignore stateful filtering for non-VPN traffic. This stops Tailscale from intercepting and blocking forwarded Docker bridge network packets:
# Start Tailscale back up
sudo systemctl start tailscaled
# Disable stateful filtering
sudo tailscale set --stateful-filtering=falseThis configuration persists across system restarts and keeps your Tailscale VPN active while ensuring Docker containers (and DNS forwarders like Pi-hole) have uninterrupted access to the internet.
If the issue persists, verify that your host is not dropping packets due to strict Reverse Path Filtering (rp_filter=1). On some Linux systems (like Alpine or custom kernels), strict mode drops packets if the return route doesn't match the incoming interface.
Set rp_filter to loose mode (2):
sudo sysctl -w net.ipv4.conf.all.rp_filter=2
sudo sysctl -w net.ipv4.conf.default.rp_filter=2To make this permanent, add these lines to /etc/sysctl.conf.