I run a small network at home: a server, a NAS, a Raspberry Pi or three, a GL.iNet router that travels with me, and more phones and tablets than I want to count - mine, and my daughter’s. Everything that can run Tailscale, runs Tailscale. Last time I counted it was about a dozen devices, and the list only grows.
The idea I have the most time for is the mesh itself. Every machine gets a stable
name and address on a private network, and reaching it stops depending on where it
is or what is in the way. My NAS sits behind my home router with no port forwarded,
ever. My server is a VM in somebody else’s datacenter. My daughter’s tablet is on
the school’s Wi-Fi. Same names, same rules, all of it mine - WireGuard underneath,
MagicDNS for the names, ACLs for when I want to be strict about who talks to what.
No port forwarding, no dynamic DNS, no exposed SSH port on anything I own. And the
company publishes its clients as open source, plus the tsnet library this whole
setup leans on - a networking stack shipped as something other apps can embed is a
thing worth crediting, and I do not think that gets said often enough.
There is one problem with all of this, and it is Android’s: a phone gets exactly one VPN slot.
Mine is always on, and it is staying on - the VPN is how I reach anything at all when I am away. The Tailscale app wants that one slot for the whole phone, for everything, all the time. Android does not let the two coexist, and Tailscale’s own FAQ says so plainly. For a phone whose VPN is not negotiable, that has always been the end of the conversation.
This post is the way out of it, and it is not a second VPN. It is an SSH client whose Tailscale node runs inside the app, as a userspace node: no VPN service, no root, nothing touched in the phone’s routes or DNS. My VPN keeps its slot. The tailnet is reachable inside the app, and nowhere else - which turns out to be exactly the shape I want.
Disclosure. I build SSH Manager Pro, the Android SSH client used in the walkthrough. The ideas apply to any client that embeds its own Tailscale node; the screens and field names are ours.
What you need
- A tailnet, with the machines you care about already joined to it. (A self-hosted control server works too - more on that below.)
- The app, with its Tailscale mode, which is free.
- MagicDNS on, so machines have names. Short names work (
attic-nas); the fully-qualified form isattic-nas.<your-tailnet>.ts.net. Without MagicDNS, use the machine’s100.x.y.zaddress wherever this post uses a name. - The machine you want to reach running
sshd, with a key or password you can log in with. Tailscale answers “how do I reach this machine”; it does not authenticate the SSH session.
Give the app a node
Settings → Tailscale is where the node lives. There are two ways to authorize it, and the first one is the one I would pick.
Sign in with your browser. Leave the Tailscale Auth Key field blank and tap Start Node. A “Sign in to Tailscale” banner appears with an Authorize in Browser button; the browser tab that opens is Tailscale’s own sign-in page, and once you approve the node there it connects by itself. Nothing to generate, nothing to copy, and nothing to leak.
Or paste an auth key. Useful when you want the node pre-authorized, or when you are setting this up on a phone that is not yours - my daughter’s tablet was this one. Generate the key from the Keys page in Tailscale’s admin console and mind the two options:
- Reusable if the node will be started more than once; a one-off key authorizes a single connection and then dies, which reads as a mysterious failure later.
- Ephemeral only if the node should disappear from the tailnet when it disconnects. For a device you want to keep finding under the same identity, leave it off.
Paste the key (it starts with tskey-auth-) into Settings → Tailscale → Tailscale
Auth Key and tap Start Node. The key is held in the app’s encrypted vault
like any other credential.
One line for the self-hosted crowd: the Control Plane URL field in the same screen points the node at Headscale or any compatible control server instead of Tailscale’s hosted service. Both authorization paths above work the same way against it.
Tags, if your tailnet has rules
A tailnet of one is simple. Mine is not - there is a tablet that should reach the NAS and nothing else, and a router that should be reachable from everywhere but should not initiate anything. That is what ACLs are for, and the way a node gets its identity in those rules is a tag, set when the key is generated.
If your ACLs are tag-based, choose the tag on the key you generate, and the node arrives already covered by the policy. The failure worth knowing about is the quiet one: the node connects, the app says it is running, and no peers are visible, because the policy grants a tagged device nothing or grants it the wrong machines. If the node is up but the tailnet looks empty, read the ACLs before you read the app. The setup guide’s troubleshooting section (/docs/tailscale-setup/) covers the vocabulary the admin console uses.
Reach the machine
With the node running, adding a machine is the part that still feels like cheating:
- Hosts → add a host (or open the one you want), and set its connection type to Tailscale.
- If the node is not running, the screen says so - “Tailscale isn’t running on this phone” - with a Set up Tailscale button that walks you through the section above without losing what you have typed. When it comes back, it says “Tailscale is running - pick a device”.
- Pick the machine from your tailnet. The device list comes from the tailnet
itself, so machine names arrive as names -
attic-nas, not an address you copy from somewhere. If you would rather type it, the Tailnet Host field takes the MagicDNS name or the100.x.y.zaddress. - Connect. That is the whole thing. There is no port forwarding on my router, no dynamic DNS record pointing at my house, no SSH port open to the internet - there never was, and there never will be.
Once you are in, the rest of the app is the rest of the app: a terminal, files over SFTP, and the port forwards that make a browser on the phone able to reach services on the tailnet. The second post in this series is what that looks like in practice - photos off the phone and Jellyfin in its browser, with the VPN untouched the whole time.
When it does not work
- The sign-in banner never appears. The node may already be authorized from a previous run, or a stale key was rejected silently - the error text under the Start and Stop buttons is the honest one.
- The auth key is rejected. One-off keys work exactly once. Generate a new one, or switch to browser sign-in.
- The node runs but the tailnet looks empty. ACLs, as above. A tagged key restricted by policy may connect and still be granted nothing.
- A self-hosted control server refuses. Check the Control Plane URL is reachable from the phone and matches your server exactly, scheme included.
- The name does not resolve but the address works. MagicDNS. Use the
100.x.y.zaddress and fix the name later.
How the app does it, and where it stops
The node is Tailscale’s tsnet library running in the app’s own process: a
userspace WireGuard implementation. It does not ask Android for the VPN slot, so it
cannot take it - there is nothing to compete over. Its traffic is just an app’s
traffic, travelling the way the rest of the phone’s traffic does, through the VPN I
already run.
The honest boundary is the same one that makes it work: the tailnet is reachable inside the app - its terminal, its file browser, the forwards it holds open - and not by anything else on the phone. That is the trade, and for reaching my own machines it is the right side of it. If you want the whole phone on the tailnet - every browser tab, every app - the Tailscale app is still the right tool, and this is not a replacement for it. It is the answer for the phone whose VPN slot is already spoken for.
SSH Manager Pro is on Google Play, and the Tailscale mode is free. Corrections to this post are welcome at support@sshmanagerpro.app.