<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: DevLog</title>
    <description>The latest articles on DEV Community by DevLog (@devlog).</description>
    <link>https://dev.to/devlog</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4086093%2F5d250487-05f7-49bc-ac26-846bc42538b1.jpg</url>
      <title>DEV Community: DevLog</title>
      <link>https://dev.to/devlog</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devlog"/>
    <language>en</language>
    <item>
      <title>Running Tailscale Without sudo: The Userspace-Networking Trade-offs Nobody Mentions</title>
      <dc:creator>DevLog</dc:creator>
      <pubDate>Mon, 24 Aug 2026 08:44:15 +0000</pubDate>
      <link>https://dev.to/devlog/running-tailscale-without-sudo-the-userspace-networking-trade-offs-nobody-mentions-16a3</link>
      <guid>https://dev.to/devlog/running-tailscale-without-sudo-the-userspace-networking-trade-offs-nobody-mentions-16a3</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;That's when Tailscale's &lt;code&gt;userspace-networking&lt;/code&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install: the easy part
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;tailscale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;code&gt;tailscaled&lt;/code&gt; registers as a system service, but in userspace mode you run it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;nohup &lt;/span&gt;tailscaled &lt;span class="nt"&gt;--tun&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;userspace-networking &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--statedir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.tailscale &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--socket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.tailscale/tailscaled.sock &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key detail: point &lt;code&gt;statedir&lt;/code&gt; and &lt;code&gt;socket&lt;/code&gt; somewhere under &lt;code&gt;$HOME&lt;/code&gt;. Everything else about this setup flows from that one decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha #1: the socket path
&lt;/h2&gt;

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

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tailscale &lt;span class="nt"&gt;--socket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.tailscale/tailscaled.sock status
tailscale &lt;span class="nt"&gt;--socket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.tailscale/tailscaled.sock ip
tailscale &lt;span class="nt"&gt;--socket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.tailscale/tailscaled.sock &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--ssh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typing that path five times a day gets old fast. A shell alias fixes it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'tailscale --socket=$HOME/.tailscale/tailscaled.sock'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wish I'd set that up on day one instead of week two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha #2: it's not a full VPN — it's a SOCKS5 proxy
&lt;/h2&gt;

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

&lt;p&gt;Here's what's actually happening under &lt;code&gt;--tun=userspace-networking&lt;/code&gt;: it's &lt;strong&gt;not&lt;/strong&gt; a system-level VPN. Traffic moves through a &lt;strong&gt;SOCKS5 proxy&lt;/strong&gt;, 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, &lt;code&gt;curl --socks5&lt;/code&gt;, and so on. A real VPN is a highway all traffic uses; this is a special pass one road accepts.&lt;/p&gt;

&lt;p&gt;So: inbound SSH to my machines, perfect. Arbitrary apps reaching the tailnet, manual configuration per app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotcha #3: reboots
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;nohup&lt;/code&gt; keeps the daemon alive after you close the terminal, but it isn't registered with &lt;code&gt;launchd&lt;/code&gt;. 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.&lt;/p&gt;

&lt;p&gt;Also worth knowing: in userspace mode, the machine can accept &lt;strong&gt;inbound&lt;/strong&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What worked out of the box
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  The honest scorecard
&lt;/h2&gt;

&lt;p&gt;No-root constraint forces trade-offs, and this is what they look like:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

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




&lt;p&gt;&lt;em&gt;This post is based on a first-hand work log, written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tailscale</category>
      <category>vpn</category>
      <category>networking</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
