Homelab: Tailscale + Syncthing
Bidirectional file sync between a home Linux server and a macOS laptop over a private encrypted network.
Overview
The goal: keep files in sync between a Raspberry Pi server at home and a MacBook, reliably, even when networks change (coffee shop Wi-Fi, campus networks, university VPN, CGNAT at home). The setup has two layers:
- Tailscale: gives both machines a private, authenticated, encrypted network (WireGuard-based), so they can always reach each other.
- Syncthing: continuously syncs designated folders over that network. Files exist as true local copies on both machines—no mounts, no kernel extensions, no stale connections.
Why this approach
- Reliability: Syncthing reconnects automatically after network changes. No stale mounts, no watchdog scripts.
- No kernel extensions: unlike SSHFS/macFUSE, Syncthing runs entirely in userspace.
- VPN resilience: using Tailscale IPs (not DNS names) ensures connectivity survives university VPN (GlobalProtect) disruptions.
- True local files: files exist on both machines. Editors, scripts, and Finder work normally even when offline.
- Bidirectional sync: changes on either side propagate to the other automatically.
- Security: all traffic is encrypted end-to-end via both Tailscale (WireGuard) and Syncthing (TLS).
Previous approach (SSHFS)
The original setup used SSHFS + macFUSE to mount the Pi's storage as a local directory on the Mac, with a watchdog script to detect and recover broken mounts. This was retired because:
- macFUSE kernel extensions became increasingly problematic on modern macOS.
- The university VPN (GlobalProtect) frequently disrupted Tailscale, causing the mount to go stale.
- Recovery required manual intervention or a complex watchdog with exponential backoff.
- Files weren't truly local—any network hiccup meant editors and scripts would hang.
Architecture
MacBook (~/homelab/)
|
| Syncthing (port 22000)
| over Tailscale private network
|
Raspberry Pi (/media/idohaber/storage)
Mac Tailscale IP: (run `tailscale ip -4`)
Pi Tailscale IP: <pi-tailscale-ip>
Both devices are on the same Tailscale network. Syncthing connects using Tailscale IPs directly, bypassing DNS resolution issues caused by VPN interference.
Prerequisites
- Raspberry Pi: Linux server with sudo access and an external storage drive mounted.
- MacBook: macOS with Homebrew installed.
- Tailscale: both devices authorized on the same tailnet.
- SSH keys: an SSH keypair on the Mac for remote administration (recommended: Ed25519).
Generate an SSH key (macOS)
ssh-keygen -t ed25519 -a 64 -C "homelab-mac"
Step 1 — Tailscale setup
Install on the Pi
# 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 connectivity:
ping <pi-tailscale-ip>
ssh <user>@<pi-tailscale-ip>
Step 2 — SSH hardening (Pi)
SSH is used for remote administration, not file sync (Syncthing handles that). Still worth hardening.
Ensure OpenSSH server is installed and running
sudo apt-get update
sudo apt-get install -y openssh-server
sudo systemctl enable --now ssh
Add your Mac's public key
# From macOS:
ssh-copy-id <user>@<pi-tailscale-ip>
Recommended sshd_config settings
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no
Then restart SSH:
sudo systemctl restart ssh
Step 3 — Syncthing on macOS
Install and start
# Install via Homebrew
brew install syncthing
# If using the dotfiles repo, stow the launchd plist
cd ~/.dotfiles && stow syncthing
# Load the service (runs on login, auto-restarts)
launchctl load ~/Library/LaunchAgents/com.idohaber.syncthing.plist
Verify
Open the Syncthing web UI:
open http://localhost:8384
The web UI should load. Note your Device ID (Actions > Show ID)—you'll need it when pairing with the Pi.
Helper script
A control script is included in the dotfiles at bin/syncthing-ctl.sh:
syncthing-ctl.sh status # Check if running, show device ID
syncthing-ctl.sh start # Load the launchd agent
syncthing-ctl.sh stop # Unload the launchd agent
syncthing-ctl.sh restart # Stop then start
syncthing-ctl.sh open # Open web UI in browser
syncthing-ctl.sh log # Tail the log file
syncthing-ctl.sh setup-pi # Print Pi setup instructions
Step 4 — Syncthing on the Pi
Install and enable
sudo apt update && sudo apt install -y syncthing
sudo systemctl enable syncthing@idohaber
sudo systemctl start syncthing@idohaber
Access the Pi's web UI
The Pi's Syncthing UI listens on localhost only, so access it via an SSH tunnel. On the Mac, open a new terminal tab:
ssh -L 8385:localhost:8384 raspberrypi
Then open http://localhost:8385 in your Mac's browser. Port 8385 is used because 8384 is already taken by the Mac's Syncthing.
Step 5 — Pairing devices and sharing folders
Exchange device IDs
- Mac UI (localhost:8384): Actions > Show ID — copy it.
- Pi UI (localhost:8385): Actions > Show ID — copy it.
Add devices
On the Mac UI:
- Click "Add Remote Device".
- Paste the Pi's device ID.
- Set the address to tcp://<pi-tailscale-ip>:22000 (Pi's Tailscale IP).
- Save.
On the Pi UI:
- Click "Add Remote Device" (or accept the pending request).
- Paste the Mac's device ID.
- Set the address to tcp://<mac-tailscale-ip>:22000.
- Save.
Share a folder
On the Pi UI:
- Click "Add Folder".
- Set Folder Path to /media/idohaber/storage.
- Under the Sharing tab, check the Mac device.
- Save. Note the Folder ID—it must match on both sides.
On the Mac UI:
- If a popup appears, accept and set the path. If not, add the folder manually:
- Click "Add Folder".
- Set the Folder ID to exactly match what the Pi shows.
- Set Folder Path to /Users/idohaber/homelab.
- Under the Sharing tab, check the Pi device.
- Save.
Day-to-day usage
Syncthing runs in the background on both machines. No manual intervention needed. Files placed in the sync folder on either machine appear on the other automatically.
# Check status
syncthing-ctl.sh status
# Open Mac web UI
syncthing-ctl.sh open
# Check logs
syncthing-ctl.sh log
# Access Pi web UI (when needed)
ssh -L 8385:localhost:8384 raspberrypi
# then open http://localhost:8385
Replication checklist
- Install Tailscale on both machines, log into the same tailnet, verify connectivity with ping/ssh.
- Harden SSH on the server (key-based auth, no root login).
- Install Syncthing on the Mac (brew install syncthing) and the Pi (sudo apt install syncthing).
- Start the service on both (launchd on Mac, systemd on Pi).
- Exchange device IDs via the web UIs, using Tailscale IPs as addresses.
- Create and share a folder on the Pi, accept/create it on the Mac with matching Folder ID.
- Test: drop a file on one side, confirm it appears on the other.
Troubleshooting
- Sync not working after VPN reconnect: Syncthing handles reconnection automatically. Give it a minute, then check syncthing-ctl.sh status.
- Devices show "Disconnected": verify Tailscale is connected on both sides (tailscale status). Check that device addresses use Tailscale IPs.
- Mac service not running after reboot: run syncthing-ctl.sh start.
- Pi service not running: sudo systemctl status syncthing@idohaber, then restart if needed.
- Folder not syncing: ensure the Folder ID matches exactly on both devices. Check the Sharing tab on both sides.
- Conflict files: Syncthing creates .sync-conflict-* files when both sides change the same file simultaneously. Resolve manually.
- Can't reach Pi web UI: use the SSH tunnel method: ssh -L 8385:localhost:8384 raspberrypi, then open http://localhost:8385.