DEV Community

DevLog
DevLog

Posted on Originally published at write83676.tistory.com

Running Tailscale Without sudo: The Userspace-Networking Trade-offs Nobody Mentions

Corporate policy: no admin rights on my work laptop. My problem: I needed to reach that machine — and SSH into my other machines — from a Galaxy Tab while away. Filing a VPN request with the security team would mean paperwork and a hard no for personal tooling. And without the sudo password, installing anything system-level is off the table.

That's when Tailscale's userspace-networking mode caught my eye: a VPN that runs entirely in user space. No root, no system service, no TUN driver. It sounded too good to be true, and in a few ways, it was.

Install: the easy part

brew install tailscale
Enter fullscreen mode Exit fullscreen mode

The CLI formula installs without admin rights. (The GUI app comes as a cask — which needs sudo, so that's out.) The real work starts with the daemon: normally tailscaled registers as a system service, but in userspace mode you run it yourself:

nohup tailscaled --tun=userspace-networking \
  --statedir=$HOME/.tailscale \
  --socket=$HOME/.tailscale/tailscaled.sock &
Enter fullscreen mode Exit fullscreen mode

The key detail: point statedir and socket somewhere under $HOME. Everything else about this setup flows from that one decision.

Gotcha #1: the socket path

With the daemon running, I tried tailscale status and got a "socket not found" error. The daemon was alive — I'd just started it. What happened?

The CLI looks for the socket at the system default (/var/run/tailscale/tailscaled.sock). In userspace mode it actually lives at $HOME/.tailscale/tailscaled.sock, and the CLI won't guess. Every command needs the flag spelled out:

tailscale --socket=$HOME/.tailscale/tailscaled.sock status
tailscale --socket=$HOME/.tailscale/tailscaled.sock ip
tailscale --socket=$HOME/.tailscale/tailscaled.sock set --ssh
Enter fullscreen mode Exit fullscreen mode

Typing that path five times a day gets old fast. A shell alias fixes it:

alias ts='tailscale --socket=$HOME/.tailscale/tailscaled.sock'
Enter fullscreen mode Exit fullscreen mode

I wish I'd set that up on day one instead of week two.

Gotcha #2: it's not a full VPN — it's a SOCKS5 proxy

This is the one that cost me an evening. I turned on Tailscale's built-in SSH (ts set --ssh), connected from the Tab to the laptop's tailnet IP — worked beautifully. Then I tried browsing an internal site from a browser and... nothing.

Here's what's actually happening under --tun=userspace-networking: it's not a system-level VPN. Traffic moves through a SOCKS5 proxy, which means apps don't route through Tailscale automatically. Anything that needs the tailnet has to be pointed at the proxy explicitly — browser proxy settings, curl --socks5, and so on. A real VPN is a highway all traffic uses; this is a special pass one road accepts.

So: inbound SSH to my machines, perfect. Arbitrary apps reaching the tailnet, manual configuration per app.

Gotcha #3: reboots

nohup keeps the daemon alive after you close the terminal, but it isn't registered with launchd. Every reboot, the daemon is gone — and there's no systemd unit equivalent you can install without sudo. I lost a few mornings to "why won't it connect" before the muscle memory of re-running the launch command kicked in. A note in my shell rc file with the exact command helps more than you'd think.

Also worth knowing: in userspace mode, the machine can accept inbound connections but won't route its own outbound traffic through the tailnet by default. Testing "can I reach my own Tailscale IP" from the same machine times out — that's expected behavior, not a broken install.

What worked out of the box

  • Tailnet joined cleanly; the laptop picked up its 100.x.x.x IP immediately
  • tailscale set --ssh gives you SSH without opening port 22 or touching macOS Remote Login settings — this alone justified the setup
  • Connecting from the Galaxy Tab to the laptop over the tailnet: zero issues
  • All state and logs live under ~/.tailscale/, easy to inspect

The honest scorecard

No-root constraint forces trade-offs, and this is what they look like:

  • Works: inbound SSH via Tailscale's built-in server; brew install with no admin; SOCKS5 proxy for per-app access; survives terminal close (nohup)
  • Doesn't: system-wide VPN routing; the GUI app (needs sudo); apps auto-routing through the tailnet; surviving a reboot (no launchd without root)

Takeaways

  1. Read the mode name literally. "Userspace networking" means userspace constraints: user-level socket paths, user-launched daemons, per-app proxies.
  2. Alias the socket flag on day one. Future-you will be grateful.
  3. Official guides assume the standard install. Every doc that says "just run tailscale status" silently assumes the system daemon. Under constraints, expect one layer of translation between the docs and your reality.
  4. If you only need inbound SSH, this is nearly perfect. The proxy awkwardness only bites when you want general outbound tailnet traffic.

This post is based on a first-hand work log, written with AI assistance.

Top comments (0)