Skip to content

Instantly share code, notes, and snippets.

@rajsinghtech
Last active June 7, 2026 22:32
Show Gist options
  • Select an option

  • Save rajsinghtech/c279beb50a63f231457e7087525e8f52 to your computer and use it in GitHub Desktop.

Select an option

Save rajsinghtech/c279beb50a63f231457e7087525e8f52 to your computer and use it in GitHub Desktop.
Running Tailscale in a Network Namespace

Running Tailscale in a Network Namespace

Isolate Tailscale to specific interfaces using Linux network namespaces.

Two Approaches

Macvlan (Bare Metal)

Creates virtual interface with its own MAC on the physical NIC. Simpler, no NAT. Best for bare metal where you control the network.

ip netns add tsns
ip link add ts-mgmt link eth0 type macvlan mode bridge
ip link set ts-mgmt netns tsns
ip netns exec tsns ip addr add 192.168.1.100/24 dev ts-mgmt
ip netns exec tsns ip link set ts-mgmt up
ip netns exec tsns ip route add default via 192.168.1.1

Pros: No NAT, gets real IP on LAN Cons: Blocked in cloud (EC2 source/dest check), host can't directly reach namespace

Veth + NAT (Cloud/Universal)

Virtual ethernet pair between host and namespace. Works everywhere.

ip netns add tsns

# Create veth pair
ip link add veth-host type veth peer name veth-ns
ip link set veth-ns netns tsns

# Configure host side
ip addr add 192.168.100.1/24 dev veth-host
ip link set veth-host up

# Configure namespace side
ip netns exec tsns ip addr add 192.168.100.2/24 dev veth-ns
ip netns exec tsns ip link set veth-ns up
ip netns exec tsns ip link set lo up
ip netns exec tsns ip route add default via 192.168.100.1

# NAT namespace traffic to physical interface
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o eth0 -j MASQUERADE
iptables -A FORWARD -i veth-host -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o veth-host -j ACCEPT

Replace eth0 with your physical interface.

Run Tailscale in Namespace

ip netns exec tsns tailscaled \
  --state=/var/lib/tailscale-ns/tailscaled.state \
  --socket=/var/run/tailscale-ns/tailscaled.sock &

ip netns exec tsns tailscale \
  --socket=/var/run/tailscale-ns/tailscaled.sock \
  up --accept-routes

App Access Patterns

Option 1: Run apps inside namespace

ip netns exec tsns my-app
ip netns exec tsns curl http://100.x.x.x

Option 2: Route host traffic through namespace

Allow host apps to reach Tailscale IPs via the namespace:

# Route Tailscale CGNAT range through namespace
ip route add 100.64.0.0/10 via 192.168.100.2

# Enable forwarding in namespace
ip netns exec tsns sysctl -w net.ipv4.ip_forward=1
ip netns exec tsns iptables -t nat -A POSTROUTING -s 192.168.100.0/24 -o tailscale0 -j MASQUERADE
ip netns exec tsns iptables -A FORWARD -i veth-ns -o tailscale0 -j ACCEPT
ip netns exec tsns iptables -A FORWARD -i tailscale0 -o veth-ns -j ACCEPT

Now host apps can reach Tailscale IPs:

ping 100.x.x.x        # works from host
curl http://peer:8080  # works from host

Systemd Service

# /etc/systemd/system/tailscaled-ns.service
[Unit]
Description=Tailscale in Network Namespace
After=network.target

[Service]
ExecStartPre=/usr/local/bin/setup-tsns.sh
ExecStart=/usr/sbin/ip netns exec tsns /usr/sbin/tailscaled \
  --state=/var/lib/tailscale-ns/tailscaled.state \
  --socket=/var/run/tailscale-ns/tailscaled.sock
Restart=on-failure

[Install]
WantedBy=multi-user.target

Real-Time Interface Isolation

For systems with timing-sensitive interfaces (motor control, PCIe fabrics, sensor networks):

# Create namespace with only management interface
ip netns add tsns
ip link add ts-mgmt link eth0 type macvlan mode bridge  # eth0 is management
ip link set ts-mgmt netns tsns
# ... configure IP

# Tailscale runs in tsns, sees only ts-mgmt
# Real-time interfaces (eth_rt0, pcie0) stay in root namespace
# Tailscale cannot touch them

DNS Configuration

Tailscale's MagicDNS won't automatically work since systemd-resolved runs in the host namespace. Configure DNS before creating the namespace:

# Create resolv.conf BEFORE namespace setup
mkdir -p /etc/netns/tsns
cat > /etc/netns/tsns/resolv.conf << 'EOF'
nameserver 100.100.100.100
search your-tailnet.ts.net
EOF

The /etc/netns/<name>/resolv.conf is automatically bind-mounted into the namespace on each ip netns exec.

Important: The file must exist before you run ip netns exec. If you create it after, it won't apply until the next ip netns exec call.

Verify:

ip netns exec tsns cat /etc/resolv.conf
ip netns exec tsns nslookup peer-hostname
ip netns exec tsns ping peer-hostname

Notes

  • Tailscale in namespace cannot see or use interfaces outside
  • All traffic (STUN, DERP, WireGuard) confined to namespace interfaces
  • Subnet routes work with --accept-routes
  • DNS requires explicit configuration (see above)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment