tailroute.

alpha v0.8.17

tailroute-cli manual · apache-2.0 · macOS 15+ · free & open source

On this page · name · the problem · synopsis · description · states · browser tunnels · what it cannot fix · security · diagnostics · see also

Name

tailroute—automatic Tailscale + VPN coexistence daemon for macOS

The problem

Most commercial VPNs on macOS don't support split tunneling. The moment the VPN connects, it takes the default route and the DNS path with it: MagicDNS becomes unreachable, *.ts.net names stop resolving, and some VPNs also drop traffic to Tailscale's CGNAT range entirely. You are left choosing between internet and your peers—or hand-editing --accept-dns every time.

tailroute refuses the choice. It watches the routing table and makes the same call a careful admin would make, every time:

MESHdns on · proxy off COEXISTdns off · proxy :1055 IDLEwatches · changes nothing vpn connects vpn disconnects tailscale quits starts · no vpn starts · vpn on MESHdns on · proxy off COEXISTdns off · proxy :1055 IDLEwatches · changes nothing vpn connects vpn disconnects tailscale quits starts ·no vpn starts ·vpn on

fig. 1—the decision state machine; route-monitor events replayed below

…

Synopsis

$ brew install shrwnsan/tap/tailroute-cli
$ sudo tailroute install
$ tailroute status

Menu bar app (same engine, native UI, free during early access): brew install --cask shrwnsan/tap/tailroute

No Homebrew? Download the DMG ↗ · sha256 published on the release · unsigned—macOS will ask you to approve the first launch

Description

tailroute is a launchd daemon that watches the macOS routing table. When a commercial VPN connects, it disables Tailscale's MagicDNS so internet traffic keeps flowing through the VPN, and starts a SOCKS5 proxy on 127.0.0.1:1055 so Tailscale peers stay reachable through the mesh. When the VPN disconnects, it re-enables MagicDNS and stops the proxy. Decisions are event-driven (route monitor) with a 60-second re-assert.

It manages exactly one thing and touches nothing else. If state is ambiguous (e.g. two VPNs active), it logs a warning and changes nothing.

States

ConditionMagicDNSSOCKS5 proxyElse
Tailscale + VPN activeoffup (127.0.0.1:1055)—
Tailscale onlyonstopped—
No Tailscale——keeps watching

Browser tunnels

Register a peer's Tailscale Serve dashboard once and open it in any browser with a valid .ts.net certificate—no extensions, no warnings. Tunnels are launchd-managed and adapt automatically: SOCKS5 path when the VPN is up, direct otherwise.

$ tailroute tunnel add mypeer

→ https://mypeer.yourtailnet.ts.net:8443

What it cannot fix

VPN Network Extensions may still block traffic to Tailscale's CGNAT range (100.64/10) at the packet level. Tested and blocked: Mullvad (WireGuard), NordVPN (NordLynx, OpenVPN, NordWhisper). The SOCKS5 proxy and tunnels are the supported paths when that happens. While the VPN is active, MagicDNS names do not resolve—use 100.x addresses or a tunnel.

Report your VPN's behavior: open a vpn-compat issue

Security

Diagnostics

$ tailroute status

daemon: running · tailscale: utun4 · vpn: utun3 · magicdns: disabled (vpn_active) · last check: 12s ago

Preview without changes: tailroute --dry-run. Logs: tail -f /var/log/tailroute.log.

See also