How to Run Tailscale Subnet Router for VPS
Make your VPS advertise private IP ranges to your tailnet, approve the route, persist IP forwarding after reboot, and use --accept-routes for Linux.
Wetin Tailscale subnet router dey do
Tailscale subnet router na one machine wey dey advertise one complete range of private IP addresses to your tailnet, so every device for the tailnet fit reach addresses for that range even though nothing for there dey run Tailscale. Your tailnet na your private Tailscale network: na all the devices wey sign in to one account or organisation. Exit node na the feature wey people dey mix am up with, and e dey do opposite work. E dey send all the traffic from one device go through the VPS, so the VPS become the route wey that device dey use reach public internet.
One sentence for each. Subnet router make one private network reachable from the tailnet. Exit node change where your public traffic dey leave from. If na the second one you want, read how to run Tailscale exit node for VPS instead. Dem be separate flags, and one VPS fit run both at the same time, but dem dey solve different problems and dem fit fail for different ways.
When VPS need subnet router
The common case na private network wey your provider don already give you. Your VPS get public address and another interface for private segment, while the other servers for that segment no get public address at all: database for 10.0.0.20, backup target for 10.0.0.30. Put Tailscale for one VPS, advertise 10.0.0.0/24, and your laptop fit reach those private addresses directly. Nothing else for the segment go change, and the database still no get public address. If the only thing you need from that segment na one web app for one port, advertising the whole range pass wetin the work require, and Tailscale serve go put HTTPS for that single port instead. The same reasoning apply to daemon wey deliberately bind to localhost only, like dsh wey dey run headless under systemd, where tailnet address for that VPS replace the SSH tunnel wey you for otherwise keep open to reach its UI.
The other case na network wey dey for the other side of the VPS. E fit be home or office LAN (local area network) behind its own router, or rack of appliances wey no fit run Tailscale at all, like managed switch or old NAS with locked firmware. One Linux box for that network become subnet router for every other device wey dey there. For house, that box often dey be small VM for hypervisor wey you already dey run, and the cost math of Proxmox host for house compared with rented VPS na the thing you need settle before you decide which side of the tunnel your services suppose dey.
Both cases get one requirement in common. The subnet router must already fit reach the range wey e advertise, using its own routing table and its own firewall. Tailscale no build that connection. E carry traffic go the router, then hand am over to the kernel to forward.
Install Tailscale and first check the local route
curl -fsSL https://tailscale.com/install.sh | shThe script dey detect the distribution, add Tailscale package repository, install the tailscale command and tailscaled daemon, then enable the service. Confirm am with systemctl is-active tailscaled; e suppose print active.
Before anything else, prove say the VPS fit reach the network wey you plan to advertise.
ip route show
ping -c3 10.0.0.20ip route show must list the private range for a real interface, something like 10.0.0.0/24 dev enp7s0 proto kernel scope link src 10.0.0.5. If ping fail here, for the router itself, no Tailscale flag go fix am. The problem dey for VPS network configuration or firewall for the target host. Fix that one first, because every later test depend on am.
Turn on IP forwarding, and make e survive reboot
Linux machine dey drop any packet wey no address to itself unless forwarding dey on. Forwarding packets for other machines na the whole work of subnet router, so this step no be optional.
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.confCheck am with sysctl net.ipv4.ip_forward, wey suppose print net.ipv4.ip_forward = 1.
People often dey do this step halfway correct. sudo sysctl -w net.ipv4.ip_forward=1 go work immediately, but e go disappear for next boot. So subnet router fit run for weeks, then stop the morning after kernel upgrade reboot. The confusing part be say nothing look broken. tailscale status still show say node dey online, admin console still show say route don get approval, and clients still get the route installed. Packets dey reach VPS, but kernel dey drop dem without logging anything. Writing the values inside /etc/sysctl.d/99-tailscale.conf na wetin make dem come back after reboot.
If you advertise routes while forwarding still dey off, tailscale up go warn you at that time, with one line wey near Warning: IPv4 forwarding is disabled. Subnet routes and exit nodes may not work correctly. Read the output of that command instead of scrolling past it.
Advertise routes
sudo tailscale up --advertise-routes=10.0.0.0/24For VPS wey already sign in to your tailnet, change the setting directly instead:
sudo tailscale set --advertise-routes=10.0.0.0/24Use tailscale set for every change wey come later. If you run tailscale up again with only one flag, e go reset any flag wey you no repeat. The CLI go stop you with error say, to change settings this way, you must mention all non-default flags. tailscale set changes only one setting and leaves the others as dem be.
Put several ranges inside one comma-separated list without spaces: --advertise-routes=10.0.0.0/24,192.168.50.0/24. Every entry must be network address for CIDR notation (classless inter-domain routing, the 10.0.0.0/24 form). If you mistakenly write your own host address, 10.0.0.5/24, the command go reject am because the bits after the prefix no be zero. The error message go name the prefix wey you probably mean. To stop advertising routes, set an empty list with sudo tailscale set --advertise-routes=.
Approve route for admin console
To advertise route na request, e no be change yet. Until admin approve am, no client go receive the route and nothing for that range go reachable. Na deliberate security measure be this, because machine wey fit add itself to everybody routing table fit capture traffic for any range wey e like.
Approve am for Machines page inside admin console. VPS go show with subnet badge. Open the row, find subnets section, edit route settings, tick the route, then save.
Approval dey apply to each prefix. Advertise 10.0.0.0/24 today and 192.168.50.0/24 next month, and the new prefix go arrive unapproved while the old one still dey work. Approved route and ignored route look the same from VPS side, so check console before you debug anything else.
You fit skip the manual step with an autoApprovers block inside tailnet policy file:
{
"autoApprovers": {
"routes": {
"10.0.0.0/24": ["tag:subnet-router"]
}
}
}Then bring the node up with that tag, sudo tailscale up --advertise-routes=10.0.0.0/24 --advertise-tags=tag:subnet-router, and the route go become approved immediately after you advertise am. The tag must already dey inside tagOwners section of the same policy file. E make sense to set this up if you dey rebuild VPS from script, because rebuilt node na new node and its routes go start unapproved again.
Why Linux clients ignore the route without --accept-routes
Route don advertise am and approve am now. Your phone and Mac fit reach 10.0.0.20. Your Linux laptop no fit, and nothing for admin console show say problem dey.
To accept subnet route mean say you write entries inside client routing table. For Android, iOS, macOS, tvOS and Windows, Tailscale client dey do this for you. For Linux, e no dey do am because Linux machine often na server or router wey person configure routing table for purpose. If e silently insert /24 wey network teach am, e fit spoil traffic wey that machine already dey handle. So for Linux, you must opt in for each client:
sudo tailscale set --accept-routesThen check where the route enter:
ip route show table 52
ip route get 10.0.0.20Tailscale for Linux no dey put accepted routes inside main routing table. E dey put dem for routing table 52 and install policy rules. You fit see the rules with ip rule show for priority range 5210 to 5270. The rules send packets wey no match go that table. So ip route show by itself no go ever list 10.0.0.0/24, and person wey check only that command go conclude say --accept-routes do nothing. ip route show table 52 na the command wey show the real state, and e suppose list the advertised range for tailscale0.
One exception good make you know. If this Linux node itself be second subnet router for its own local network, --accept-routes go make am send traffic for its own directly connected subnet through the other router instead of through its own interface. For standby router inside high availability pair, leave --accept-routes off and advertise only.
Failure mode: two routers advertising overlapping ranges
Two subnet routers no suppose advertise the same ranges. Overlapping ranges wey get different prefix lengths dey allowed, and Tailscale go pick the most specific match. If router A dey advertise 10.0.0.0/24 and router B dey advertise 10.0.0.0/16, traffic go 10.0.0.20 go A.
Wetin dey surprise people na wetin happen when A go offline. Tailscale no go fall back to the less specific route. Traffic go 10.0.0.20 go stop, while traffic go 10.1.0.20 go continue through B. The symptom go look like half of the private network don go down. The cause na one offline node wey dey hold the more specific prefix. If you want failover, make the wider router advertise the narrower prefixes too, so both routers cover the same addresses.
The other overlap dey closer to the client. If you dey on hotel network for 192.168.1.0/24 while your subnet router dey advertise 192.168.1.0/24, both routes go compete for the same destinations. Which one go win depend on the platform. For Linux, install a rule before Tailscale own rule, so local addresses go use the main table:
sudo ip rule add to 192.168.1.0/24 priority 2500 lookup mainThat rule no persistent, and e go disappear for the next boot. The real fix na to choose a private range wey you no go meet for other networks. 192.168.0.0/24 and 192.168.1.0/24 na the defaults for most home routers, so pick something inside 10.0.0.0/8 wey you choose deliberately. The same collision go break plain WireGuard VPN wey you configure by hand, for the same reason: the more specific local route go win, so the traffic no go enter the tunnel.
Failure mode: DNS resolve go address wey no route cover
This one hard to debug because nothing dey report error. The name resolve. The connection just times out.
Make we say db.internal.example.com resolve go 10.0.5.20 through your private nameserver, and you advertise 10.0.0.0/24. The lookup succeed because DNS (domain name system) resolution and IP routing na separate steps, and neither one dey check the other. Then packet wey go 10.0.5.20 no find matching route for the tailnet, so e comot through the client default gateway and disappear.
Two commands fit separate the two sides:
nslookup db.internal.example.com
ip route get 10.0.5.20If the lookup return address but ip route get no answer with dev tailscale0, the name dey fine and na the route dey miss. Advertise range wey cover the address, either 10.0.0.0/16 or another explicit prefix, then approve the new prefix for the console.
The nameserver itself get similar trap. If you set global nameserver for the admin console to private address like 10.0.0.53, that address must dey inside approved route, or your devices no fit reach the resolver at all. If you turn on the option wey overrides local DNS servers while you point to resolver wey nobody fit reach, every device for the tailnet go lose name resolution at once, including the ones wey work one second before. Advertise and approve the route to the resolver first, then change the DNS setting. If DNS inside tunnel na the part wey you dey struggle with, how DNS dey break over WireGuard tunnel cover the same mechanism without the extra coordination layer.
Source NAT, and site to site links
By default, subnet router dey rewrite source address of every forwarded packet to its own private address. Na SNAT (source network address translation) be that. E dey make replies work without changing anything for the private network: database for 10.0.0.20 dey answer the VPS, wey e already know how to reach. The disadvantage be say database dey see every tailnet connection as if na VPS send am. So per-source firewall rules and access logs no go tell you anything useful.
For Linux, turn am off when you want make the client's real tailnet address remain:
sudo tailscale set --snat-subnet-routes=falseThe hosts for private network go then need route back to 100.64.0.0/10, wey be the range Tailscale dey assign to devices, with subnet router as the next hop. Without that return route, their replies go default gateway and no go reach, so connection go hang after the first packet. Add the static route for the private network's gateway, or leave SNAT on.
Site to site link na when two subnet routers dey do this at the same time. Each one advertises its own network and accepts the other router's network:
sudo tailscale up --advertise-routes=10.0.0.0/24 --snat-subnet-routes=false --accept-routesRun the matching command for the other router with its own range. The two ranges must no be the same. If large transfers dey stall while ssh and ping dey work fine, MSS (maximum segment size) na the cause. MSS na the biggest data chunk wey TCP packet fit carry. Tunnel overhead fit make forwarded packets too big for one link along the way. Clamping go fix am:
sudo iptables -t mangle -A FORWARD -o tailscale0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtuSave that rule with iptables-persistent, otherwise e go disappear for the next boot.
Housekeeping wey go keep am running
Node keys dey expire after 180 days by default, as of August 2026. When key for subnet router expire, the node go sign out and the whole advertised range go become unreachable, even though no configuration change happen anywhere to explain am. Disable key expiry for this machine for the Machines page of the admin console, then write down say you do am.
Tailscale prefer direct connection between peers and e go fall back to im relay servers when e no fit establish one. The relays dey work, but dem add latency. VPS wey get public address na the easy case: allow inbound UDP 41641 and most peers go connect directly. If ufw dey manage the firewall, the ufw rules wey VPS really need cover the syntax.
Access rules na the other half. For default tailnet, every device wey belong to you fit reach every other one, so approved route go just work. Once you write ACL policy, destination side of rule must name the private range, because 10.0.0.20 no be tailnet address and rules wey use tailnet IPs or tags no cover am.
Finally, decide whether you want coordination server wey you no dey run yourself. Tailscale control plane na hosted service. Your keys dey remain for your machines, but the account and policy file dey there. Wetin person fit actually do with compromised control plane or stolen identity login na the part wey worth settling before you give am route into your private network, and Tailscale trust model show where that boundary dey. Cost rarely na wetin make people leave am, because the free plan cover up to six users with unlimited devices wey belong to dem, although subnet router wey you bring up under tag dey counted differently from one wey sign in as you. After that point, the bill dey follow people instead of machines, so wetin household or five person team really go pay after free plan finish worth working out before you add the account wey go push you over. Running Headscale, the self hosted Tailscale control server keep that for your own VPS, but you go need maintain am. The other answer to the same worry na to leave Tailscale clients behind too, and self-hosting the NetBird VPN server put the coordination layer and im own mesh clients for one machine wey you control. If you still dey decide between this model and hand written config, the comparison of WireGuard and Tailscale explain wetin the coordination layer give you and wetin e cost.
FAQ
Wetin be di difference between a subnet router and an exit node?
A subnet router dey advertise one range of private addresses, so tailnet devices fit reach machines wey no dey run Tailscale. An exit node dey advertise itself as route to the whole internet, so device go send all im traffic through that node public address. One VPS fit do both. Dem be separate flags, --advertise-routes and --advertise-exit-node, and each one need im own approval for the admin console.
Why my Linux client dey ignore the advertised subnet route?
Linux clients no dey accept subnet routes unless you tell dem to. Run sudo tailscale set --accept-routes for the client. Then check with ip route show table 52, no be ip route show. Tailscale dey install accepted routes for routing table 52 and dey reach dem through policy rules, so main table no go list dem and working route fit look like say e no dey there.
My subnet stop working after reboot. Wetin spoil?
Na IP forwarding most likely. Value wey you set with sysctl -w no dey survive reboot, so write am to /etc/sysctl.d/99-tailscale.conf and confirm with sysctl net.ipv4.ip_forward. If forwarding dey on and the range still no dey reachable, check the node for the admin console. Node keys dey expire after 180 days by default, and expired subnet router fit look like network fault instead of account problem.
Two subnet routers fit advertise the same range?
No be identical ranges. Overlapping ranges wey get different prefix lengths dey okay, and the most specific one go win. Failover need care: when router wey dey hold the more specific prefix go offline, Tailscale no go fall back to the wider route, so that traffic go stop. For real standby pair, make both routers advertise the same specific prefixes.
The hostname dey resolve but the connection dey time out. Why?
DNS resolution and routing na separate steps. Name fit resolve to address wey no approved route cover, and packet go then comot through the client default gateway. Run ip route get <address> for the client. If the answer no include dev tailscale0, advertise range wey cover that address and approve the new prefix for the admin console.