featured image

Securing Your Personal Cloud: Implementing Tailscale for Zero-Trust Networking

This tutorial provides a comprehensive, technical deep dive into implementing Tailscale for secure, zero-trust networking in your self-hosted personal cloud environment.

Published

Sun Nov 09 2025

Technologies Used

Tailscale TrueNAS Scale Docker
Advanced 30 minutes

Setting up a self-hosted personal cloud on repurposed hardware gets you most of the way to data sovereignty. But exposing that hardware to the internet undoes a lot of the benefit. Port forwarding invites automated crawlers and brute-force bots to knock on your server’s door around the clock. Slapping Nginx on top helps, but you’re still running a public service — and managing certificates for half a dozen containerized apps (Nextcloud, Immich, Vaultwarden, Milvus) quickly becomes its own part-time job.

This is the technical walkthrough for routing traffic to TrueNAS SCALE applications and SMB shares without exposing a single port to the public web. We’re using Tailscale — a WireGuard-based mesh VPN — configured as both a subnet router and an exit node.

What You Need Before Forging the Tailnet

You should be comfortable with CIDR notation (e.g., /24 subnets), basic Docker Compose syntax, and navigating ZFS datasets and UNIX permissions. On the environment side: TrueNAS SCALE as the host OS, Dockge or Portainer for orchestration, knowledge of your local TrueNAS IP and subnet, and a free Tailscale account tied to an SSO provider (GitHub, Google, or Microsoft).

Why Not Just Use WireGuard Directly

A traditional centralized VPN like OpenVPN is a fortified bridge over a moat. Everyone must cross the bridge. If the bridge goes down, or the guard is compromised, the system fails.

Tailscale is different. It negotiates a direct peer-to-peer connection between your phone in a coffee shop and your nine-year-old gaming PC in a closet, bypassing your router’s firewall entirely. The trick is NAT traversal — specifically the separation between the control plane and the data plane.

WireGuard is performant, but it requires static endpoint IPs and open UDP ports. In a home lab, you’re sitting behind your ISP’s NAT, possibly even CGNAT. Tailscale solves this by acting as a central coordinator. When your TrueNAS server and your phone both boot up, they reach out to Tailscale’s servers via outbound HTTPS. The coordinator swaps their public keys and current dynamic IPs.

Then both devices use STUN (Session Traversal Utilities for NAT) to perform UDP hole punching — simultaneously sending packets to each other to trick their respective routers into opening a stateful firewall rule for a direct connection. Once the hole is punched, data flows peer-to-peer via kernel-space WireGuard. If hole punching fails (say, on restrictive hotel Wi-Fi), Tailscale falls back to routing through geographically distributed DERP relay servers, which keeps the connection alive at the cost of slightly higher latency.

Since Tailscale runs in userspace (written in Go, unlike native WireGuard in the Linux kernel), there’s a minor CPU overhead per packet. On repurposed gaming hardware, this is negligible.

Stateful ZFS Storage First

Tailscale needs to store its cryptographic state on disk. If the container restarts and generates a new identity, your node drops off the network. That’s why we provision a ZFS dataset before anything else.

In TrueNAS, navigate to Datasets and create a new dataset under your Docker configs pool — something like tank/configs/tailscale. Then edit the ACL permissions. Because Tailscale must run as root to manipulate network interfaces, strict root-only permissions can cause lockout issues with Dockge. Set UNIX permissions to allow Read/Write/Execute for User, Group, and Other so the Dockge user isn’t blocked from mounting the volume.

Generating the Auth Key

Log into the Tailscale Admin Console at login.tailscale.com. Navigate to Settings > Keys > Generate auth key. Toggle Reusable to ON — this lets you tear down and rebuild the stack without generating a new key each time. Leave Ephemeral OFF; we want this device to persist. Copy the generated tskey-auth-XXX string immediately.

The Docker Compose Stack

Open Dockge and create a new stack named tailscale:

version: "3.7"
services:
  tailscale:
    image: tailscale/tailscale:latest
    container_name: tailscale
    hostname: truenas-scale
    environment:
      - TS_AUTHKEY=tskey-auth-kXXXXXX...
      - TS_ROUTES=10.99.0.0/24
      - TS_EXTRA_ARGS=--advertise-exit-node
    volumes:
      - /mnt/tank/configs/tailscale:/var/lib/tailscale
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - NET_ADMIN
      - NET_RAW
    network_mode: host
    restart: unless-stopped

A few things worth explaining here:

TS_ROUTES tells Tailscale to act as a bridge to the 10.99.0.x network. This is what lets you access SMB shares using local IPs from anywhere in the world.

network_mode: host binds the container directly to TrueNAS’s network stack, completely bypassing Docker’s isolated bridge network. This is mandatory for a subnet router — without it, the container can’t route packets for the rest of the LAN.

/dev/net/tun and the cap_add entries grant the container kernel-level privileges to create virtual network interfaces and route packets. Don’t skip these; the container won’t start correctly without them.

Deploy the container.

Approving the Node in the Admin Console

By default, Tailscale doesn’t blindly trust nodes that claim to be subnet routers or exit nodes. Go back to the Admin Console, navigate to Machines, find truenas-scale, click the three dots, and select Edit route settings. Check the boxes to approve the 10.99.0.0/24 subnet and the exit node.

Then — and this is the one people always miss — click the three dots again and select Disable key expiry. By default, Tailscale keys expire every 90 days. If you skip this step, your server will silently drop off the VPN while you’re traveling, cutting off access to Vaultwarden and your SMB shares with no warning.

Subnet Collision, Split Tunneling, and Auth Key Hygiene

The subnet collision trap. If you advertise 192.168.1.0/24 — the default for 90% of consumer routers — and you connect from a coffee shop that also uses 192.168.1.0/24, your phone won’t know whether 192.168.1.5 is your NAS or the guy sitting next to you. Design your home lab around an obscure private IP range. The 10.99.0.0/24 range in this guide is unlikely to conflict with any public Wi-Fi.

Split tunnel vs. full tunnel. By default, only traffic destined for your 10.99.0.x or 100.x.x.x Tailscale IPs routes through TrueNAS. Everything else goes to local Wi-Fi. This is fast and efficient for checking files. If you’re on untrusted public Wi-Fi and want to encrypt all your traffic, select TrueNAS as an exit node in the Tailscale app — all web traffic routes through your home connection.

Auth key leakage. A reusable auth key grants immediate access to your entire setup. Treat the tskey-auth string like a production database password. Inject it into your stack, then revoke or delete the plaintext copy. Don’t leave it sitting in a notes app.

Once deployed, you can access SMB shares using local IPs from anywhere, manage Dockge containers, and sync private photos to Immich — all over an encrypted, peer-to-peer tunnel that never touches a public port.

We respect your privacy.

← View All Tutorials

Related Projects

    Ask me anything!