Daisy chain a Tailscale with a WireGuard tunnel from another VPN provider, to create a VPN exit node for your tailnet. Features include:
- Runnable in non-root containers
- Only
NET_ADMINis required wg-quickwith small fixes to run rootlessly
- Only
- containerboot compliant for proper
TS_env vars - Dual-stack IP rules included
- Kernelspace networking for container-native network access - good for running with MagicDNS and your own Pi-Hole.
- Custom v4 endpoints to not be routed through the WireGuard interface (i.e. holepunch).
minimalimage with reduced features and smaller binaries (recommended)
Tested working on rootless Podman. Please open issues to correct any footguns you found.
-
Fetch a WireGuard file from your VPN provider and format it to look like
./example.wg0.conf. The main tweak is to remove the DNS field and addPersistentKeepaliveto a low value. -
Configure
docker-compose.ymlto your own tastes and bring it up. Most of the explanations and config values are commented in there.
Specific env vars for this image:
| Name | Default | Description |
|---|---|---|
TSWG_WGCONF,WG_CONFIG |
none |
Path to your WireGuard config file |
TSWG_HOLEPUNCH_ENDPOINTS,HOLEPUNCH_ENDPOINTS |
none |
(Optional) Custom endpoints in IPv4:Port format that bypass the WireGuard tunnel. Useful for connecting to your own Headscale. |
A Dante-powered SOCKS proxy can be enabled, as a replacement for Tailscale's embedded proxy (TS_SOCKS5_PROXY) that doesn't work for now. It has the following env vars:
| Name | Default | Description |
|---|---|---|
TSWG_SOCKD_ENABLED |
Set to true to enable sockd |
|
TSWG_SOCKD_PORT |
1080 |
Port number to expose tswg on |
TSWG_SOCKD_FILE |
Mount your own sockd.conf file for advanced usage. TSWG_SOCKD_PORT will be ignored |
|
TSWG_SOCKD_TIMEIN |
3 |
Seconds to wait (since executing containerboot) until starting the socks daemon |
The default sockd.conf file is at /etc/sockd.conf and will get copied to /tmp/sockd.conf.tmp before enabling. For more information on the config file, check out its man page.
Other Tailscale env vars for Docker should also work. Some are changed from Tailscale defaults to support kernespace networking:
| Name | Default | Description |
|---|---|---|
TS_USERSPACE |
false |
Changed to false by default |
TS_DEBUG_FIREWALL_MODE |
nftables |
Force use of new nftables instead of auto mode |
main: normal Tailscale image, tracks main branchlatest: normal Tailscale image, tracks latest tagsminimalsuffix (i.e.latest-minimalandmain-minimal): Tailscale with reduced featureset and smaller binaries
latest-minimal is recommended.
-
Unlike Tailscale's Mullvad exit node, this container does not have
IsJailedandIsWireguardOnlyfunctionality toggled on for traffic restrictions. Please control traffic flows using ACL or grants. -
This image provides a feature akin to multihop, whereby the first hop is controlled at a location that you can host. Therefore, the speed is reduced, and can significantly slow down if your VPN endpoint is far away from your tswg instance.
Loadinggraph LR client --> tswg[tswg in us] --> vpn1[vpn in canada] client --> tswg[tswg in us] ---> vpn2[vpn in germany] client --> tswg[tswg in us] ----> vpn3[vpn in japan]
-
This image provides an alternative to headscale's current (lack of) WireGuard only peers implementation
- Gluetun + Tailscale has strict firewalls that conflicts with Tailscale running on non-userspace mode. If someone gets theirs working, please share (probably some postrouting rules that I haven't looked into).
- Wireproxy + tun2socks kind of worked but segfaults every 5 minutes for some reason.
- Tailguard seems to be the most similar candidate, though it can also be used for general WireGuard routing.