Homelab: Tailscale + SSHFS
A stable, encrypted link between a home Linux server and a macOS laptop (no port-forwarding).
Overview
The goal was simple: access a Linux server at home from a macOS laptop reliably, even when networks change (coffee shop Wi‑Fi, campus networks, CGNAT at home, etc.). We built it in two layers:
- Tailscale: gives the laptop and the server a private, authenticated, encrypted network (WireGuard-based), so they can always reach each other.
- SSH + SSHFS: uses that stable network to log in securely and (optionally) mount a server directory on the Mac like a local folder.
Why this approach
- Reliability: Tailscale handles NAT traversal and IP changes, so your “server is always there”.
- Security: no open inbound ports required; traffic is encrypted end-to-end; access is tied to identity.
- Convenience: SSHFS makes remote files feel local (Finder, editors, scripts) without copying back and forth.
Architecture
Once both devices are logged into the same tailnet, the Mac can reach the server via its Tailscale IP (typically 100.x.y.z) or a MagicDNS hostname (often something like server-name.tailnet-xyz.ts.net).
macOS laptop ──(Tailscale private network)──> Linux server
└── SSH (22) over tailnet
└── SSHFS mount (remote folder appears local)
Prerequisites
- Home server: Linux machine with sudo access.
- Laptop: macOS with admin access.
- Accounts: a Tailscale account (and both devices authorized on the same tailnet).
- SSH keys: an SSH keypair on the Mac (recommended: Ed25519).
Generate an SSH key (macOS)
ssh-keygen -t ed25519 -a 64 -C "homelab-mac"
Step 1 — Tailscale setup
Install on Linux server
Install Tailscale using the official instructions for your distro, then authenticate.
# After install:
sudo tailscale up
# Confirm it’s connected:
tailscale status
tailscale ip -4
Install on macOS
Install the Tailscale app on macOS and log into the same tailnet. Confirm you can reach the server.
# Quick connectivity check:
ping <server-tailscale-ip-or-magicdns-name>
ssh <user>@<server-tailscale-ip-or-magicdns-name>
Step 2 — SSH hardening (server)
The server should accept key-based logins and avoid password-based access where possible. If you already have SSH set up, treat this as a checklist.
Ensure OpenSSH server is installed and running
# Ubuntu/Debian:
sudo apt-get update
sudo apt-get install -y openssh-server
sudo systemctl enable --now ssh
Add your Mac’s public key to the server
# From macOS:
ssh-copy-id <user>@<server-tailscale-ip-or-magicdns-name>
Recommended sshd_config settings
Edit /etc/ssh/sshd_config and consider:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no
Then restart SSH:
sudo systemctl restart ssh
Step 3 — SSHFS mount on macOS
Install macFUSE + SSHFS
SSHFS on macOS typically requires macFUSE. One common approach is Homebrew.
brew install --cask macfuse
brew install sshfs
Create a mount point
mkdir -p ~/mnt/homelab
Mount a server directory
Replace <server>, <user>, and /path/on/server.
sshfs \
-o reconnect,ServerAliveInterval=15,ServerAliveCountMax=3 \
-o IdentityFile=~/.ssh/id_ed25519 \
<user>@<server>:/path/on/server \
~/mnt/homelab
Unmount
umount ~/mnt/homelab
# If that fails on macOS:
diskutil unmount force ~/mnt/homelab
Replication checklist
- Install Tailscale on server + Mac, log into same tailnet, verify ping/ssh over tailnet.
- Enable SSH server on Linux and require key-based auth.
- Copy Mac public key to the server and verify ssh user@server works without password prompts.
- Install macFUSE + SSHFS on macOS.
- Mount the desired server directory with sshfs.
Troubleshooting
- Can’t ping/SSH the server: confirm both devices show “connected” in Tailscale and are in the same tailnet; check tailscale status on the server.
- SSH asks for password: your key may not be installed, permissions may be off, or password auth is still enabled server-side.
- SSHFS disconnects: use the reconnect/keepalive options shown above; consider mounting a less busy directory.
- macFUSE permissions prompts: approve the system extension in macOS settings if required, then reboot.