Skip to content

Instantly share code, notes, and snippets.

@dannypv05261
Last active February 23, 2026 12:45
Show Gist options
  • Select an option

  • Save dannypv05261/a339618765584f0a883aee1e954704c5 to your computer and use it in GitHub Desktop.

Select an option

Save dannypv05261/a339618765584f0a883aee1e954704c5 to your computer and use it in GitHub Desktop.
bare-metal sing-box script in Debian / sing-box config with tailscale exit node inbound / tailscale mullvad VPN add-on or exit node as outbound / retrieve tailscale DNS servers

Use case:

sing-box is the edge between tailscale network and home LAN network

  1. In tailscale client app (mobile / PC), you can connect to sing-box gateway exit node to chat with AI, unlock Pixel phone full features in some special places (haha), access home network, etc
  2. In home network, you can use sing-box as gateway by any means to chat with AI, access devices in tailscale networks that is visible by sing-box-gateway

Route:

  • Every public DNS query in route -> be resolved by VPN DNS server via tunnel
  • AI / IDE traffic -> route via VPN tunnel (haha)
  • home network domain/traffic -> route to home router
  • tailscale MagicDNS domain/ tailscale traffic -> tailscale
  • The rest of traffic -> eth0

Reminder:

From offical doc, endpoints can be used as inbound and outbound, but only ts as outbound works in 12.x version, while ts as inbound only works after 1.13.x beta version (forgot).

  • Repalce tailscale auth. token YOUR_TAILSCALE_AUTH_KEY to your tailscale auth key
  • Replace exit node IP addresses 100.x.x.x to exit node you want to use it as outbound. It can be tailscale IP address of your own exit node or tailscale mullvad VPN add-on's exit node (May check with command tailscale exit-node list), or you can also switch to Wireguard
  • Replace yourdomain.com with your own domain if you want sing-box to resolve your home LAN domains, or you can remove the entire block
  • Replace advertise_routes 192.168.x.0/24 with your subnet if you want to connect to your home LAN netork. Just keep it in awaiting for approval but it is required
  • Change network interface eth0 if required (use ifconfig cmd to check)
  • Delete route { "ip_version": 6, "outbound": "ts-ep-mullvad-hk" } and switch all ipv4_only in DNS module to prefer_ipv4 if your ISP & local network support to reach site with IPv6 address
  • You can enable cache and change log.level to "warn" in production prefer_ipv4

Prerequisite

Set IP forward

# Debian
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf

Tailscale settings

(Optional) set dns-ts-mullvads-xx to Mullvad VPN Setting if you use tailscale mullvad VPN add-on as VPN servers

Setting -> Mullvad VPN -> add dns-ts-mullvads-xx to list

(Optional) Tailscale ACL

Tagging:

  • sing-box-gateway: tag:network
  • devices wants to back home: tag:home

Expose sing-box DNS module access to other tailscale devices / tailscale DNS

"grants": [
  //.......
  // MagicDNS, All users and devices can access tag:dns for DNS query. Mark sing-box-gateway with network tag
  {
    "src": ["*"],
    "dst": ["tag:network"],
    "ip":  ["udp:53"],
  }
  //.......
],

Allow tailscale devices to access home LAN network

"grants": [
  //.......
		// All tag:home can access 192.168.x.0/24 via exit node with tag:home
		{
			"src": ["tag:home"],
			"dst": ["192.168.x.0/24"],
			"via": ["tag:home", "tag:network"],
			"ip":  ["*"],
		},
  //.......
],
{
"log": {
"level": "trace",
"timestamp": true
},
"dns": {
"independent_cache": true,
"disable_cache": false,
"disable_expire": false,
"servers": [
{
"tag": "dns-local",
"type": "local"
},
{
"tag": "dns-router",
"type": "udp",
"server": "192.168.x.x",
"server_port": 53,
"detour": "direct"
},
{
"tag": "dns-cloudflare-bootstrap",
"type": "https",
"server": "1.1.1.1"
},
{
"tag": "dns-ts",
"type": "tailscale",
"endpoint": "ts-ep-gateway"
},
{
"tag": "dns-ts-cloudflare-hk",
"type": "https",
"server": "1.1.1.1",
"detour": "ts-ep-mullvad-hk"
},
{
"tag": "dns-ts-mullvads-hk",
"type": "udp",
"server": "10.64.0.1",
"server_port": 53,
"detour": "ts-ep-mullvad-hk"
},
{
"tag": "dns-ts-mullvads-jp",
"type": "udp",
"server": "10.64.0.1",
"server_port": 53,
"detour": "ts-ep-mullvad-jp"
}
],
"rules": [
{
"rule_set": [
"geosite-anthropic",
"geosite-claude",
"geosite-gemini",
"geosite-jetbrains",
"geosite-openai"
],
"server": "dns-ts-mullvads-jp",
"strategy": "prefer_ipv4"
},
{
"domain_suffix": [
"yourdomain.com"
],
"server": "dns-router",
"strategy": "ipv4_only",
"disable_cache": false
},
{
"domain_suffix": [
"ts.net"
],
"server": "dns-ts",
"strategy": "ipv4_only"
}
],
"final": "dns-ts-mullvads-jp",
"strategy": "ipv4_only"
},
"ntp": {
"enabled": true,
"server": "time.cloudflare.com",
"server_port": 123,
"interval": "30m",
"domain_resolver": "dns-cloudflare-bootstrap"
},
"endpoints": [
{
"type": "tailscale",
"tag": "ts-ep-gateway",
"state_directory": "/opt/sing-box-tailscale/states/tailscale-gateway",
"auth_key": "YOUR_TAILSCALE_AUTH_KEY",
"hostname": "sing-box-gateway",
"accept_routes": true,
"advertise_routes": ["192.168.x.0/24"],
"advertise_exit_node": true,
"udp_timeout": "5m",
"domain_resolver": "dns-cloudflare-bootstrap"
},
{
"type": "tailscale",
"tag": "ts-ep-mullvad-jp",
"state_directory": "/opt/sing-box-tailscale/states/tailscale-mullvad-jp",
"auth_key": "YOUR_TAILSCALE_AUTH_KEY",
"hostname": "sing-box-mullvad-jp",
"accept_routes": false,
"exit_node": "100.x.x.x",
"exit_node_allow_lan_access": false,
"udp_timeout": "5m",
"domain_resolver": "dns-cloudflare-bootstrap"
},
{
"type": "tailscale",
"tag": "ts-ep-mullvad-hk",
"state_directory": "/opt/sing-box-tailscale/states/tailscale-mullvad-hk",
"auth_key": "YOUR_TAILSCALE_AUTH_KEY",
"hostname": "sing-box-mullvad-hk",
"accept_routes": false,
"exit_node": "100.x.x.x",
"exit_node_allow_lan_access": false,
"udp_timeout": "5m",
"domain_resolver": "dns-cloudflare-bootstrap"
}
],
"inbounds": [
{
"type": "socks",
"tag": "socks-in",
"listen": "0.0.0.0",
"listen_port": 1080,
"sniff": true,
"users": [
{
"username": "user",
"password": "pass"
}
]
},
{
"type": "tun",
"tag": "tun-in",
"address": [
"172.19.0.1/30"
],
"mtu": 1500,
"auto_route": true,
"strict_route": true,
"stack": "system"
},
{
"type": "direct",
"tag": "dns-in",
"listen": "::",
"listen_port": 53
}
],
"outbounds": [
{
"type": "direct",
"tag": "direct-bootstrap",
"bind_interface": "eth0",
"domain_resolver": "dns-cloudflare-bootstrap"
},
{
"type": "direct",
"tag": "direct",
"bind_interface": "eth0"
}
],
"route": {
"rules": [
{
"action": "sniff",
"sniffer": [
"http",
"tls",
"quic",
"dns"
]
},
{
"type": "logical",
"mode": "or",
"rules": [
{
"port": 53
},
{
"protocol": "dns"
}
],
"action": "hijack-dns"
},
{
"rule_set": [
"geosite-anthropic",
"geosite-claude",
"geosite-gemini",
"geosite-jetbrains",
"geosite-openai"
],
"outbound": "ts-ep-mullvad-jp"
},
{
"action": "resolve"
},
{
"ip_is_private": true,
"ip_version": 4,
"outbound": "direct"
},
{
"ip_is_private": true,
"ip_version": 6,
"action": "reject"
},
{
"ip_cidr": [
"100.64.0.0/10"
],
"outbound": "ts-ep-gateway"
},
{
"ip_version": 6,
"outbound": "ts-ep-mullvad-hk"
}
],
"rule_set": [
{
"type": "remote",
"tag": "geosite-anthropic",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/refs/heads/rule-set/geosite-anthropic.srs",
"download_detour": "direct-bootstrap",
"update_interval": "1h"
},
{
"tag": "geosite-claude",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-anthropic.srs",
"download_detour": "direct-bootstrap",
"update_interval": "1h"
},
{
"tag": "geosite-cn",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-cn.srs",
"download_detour": "direct-bootstrap",
"update_interval": "1h"
},
{
"tag": "geosite-gemini",
"type": "remote",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/refs/heads/rule-set/geosite-google-gemini.srs",
"download_detour": "direct-bootstrap",
"update_interval": "1h"
},
{
"tag": "geosite-jetbrains",
"type": "remote",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/refs/heads/rule-set/geosite-jetbrains-ai.srs",
"format": "binary",
"download_detour": "direct-bootstrap",
"update_interval": "1h"
},
{
"type": "remote",
"tag": "geosite-openai",
"format": "binary",
"url": "https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-openai.srs",
"download_detour": "direct-bootstrap",
"update_interval": "1h"
}
],
"final": "direct",
"default_domain_resolver": {
"server": "dns-local",
"rewrite_ttl": 60,
"strategy": "ipv4_only"
}
},
"experimental": {
"cache_file": {
"enabled": false,
"path": "/opt/sing-box-tailscale/states/sing-box/cache.db",
"store_fakeip": false,
"rdrc_timeout": "1d"
}
}
}
#!/bin/sh
sudo tee /etc/systemd/system/sing-box.service > /dev/null << 'EOF'
[Unit]
Description=sing-box transparent-proxy
After=network-online.target
Wants=network-online.target
[Service]
ExecStartPre=/usr/bin/rm -f /opt/sing-box-tailscale/bin/sing-box/states/sing-box/cache.db
ExecStart=/opt/sing-box-tailscale/bin/sing-box run -c /opt/sing-box-tailscale/sing-box.json
Restart=on-failure
RestartSec=5
User=root
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now sing-box
#!/bin/sh
# Run with root access
# Just change the version here if you want to upgrade
# You can check version in offical github https://github.com/SagerNet/sing-box/releases
VERSION=1.13.0-rc.4
sudo mkdir -p /opt/sing-box-tailscale/bin
cd /opt/sing-box-tailscale/bin
sudo curl -L "https://github.com/SagerNet/sing-box/releases/download/v${VERSION}/sing-box-${VERSION}-linux-amd64.tar.gz" -o sing-box.tar.gz
sudo tar xzf sing-box.tar.gz --strip-components=1 && sudo rm sing-box.tar.gz
sudo /opt/sing-box-tailscale/bin/sing-box version
sudo mkdir -p /opt/sing-box-tailscale/states/sing-box
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment