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>
Tip: Use the Tailscale IP (e.g. <pi-tailscale-ip>) rather than MagicDNS names. This avoids DNS resolution failures when the university VPN is active.

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

  1. Mac UI (localhost:8384): Actions > Show ID — copy it.
  2. Pi UI (localhost:8385): Actions > Show ID — copy it.

Add devices

On the Mac UI:

  1. Click "Add Remote Device".
  2. Paste the Pi's device ID.
  3. Set the address to tcp://<pi-tailscale-ip>:22000 (Pi's Tailscale IP).
  4. Save.

On the Pi UI:

  1. Click "Add Remote Device" (or accept the pending request).
  2. Paste the Mac's device ID.
  3. Set the address to tcp://<mac-tailscale-ip>:22000.
  4. Save.

Share a folder

On the Pi UI:

  1. Click "Add Folder".
  2. Set Folder Path to /media/idohaber/storage.
  3. Under the Sharing tab, check the Mac device.
  4. Save. Note the Folder ID—it must match on both sides.

On the Mac UI:

  1. If a popup appears, accept and set the path. If not, add the folder manually:
  2. Click "Add Folder".
  3. Set the Folder ID to exactly match what the Pi shows.
  4. Set Folder Path to /Users/idohaber/homelab.
  5. Under the Sharing tab, check the Pi device.
  6. Save.
Important: The Folder ID must be identical on both devices. This is what links them. The folder label can differ.

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

  1. Install Tailscale on both machines, log into the same tailnet, verify connectivity with ping/ssh.
  2. Harden SSH on the server (key-based auth, no root login).
  3. Install Syncthing on the Mac (brew install syncthing) and the Pi (sudo apt install syncthing).
  4. Start the service on both (launchd on Mac, systemd on Pi).
  5. Exchange device IDs via the web UIs, using Tailscale IPs as addresses.
  6. Create and share a folder on the Pi, accept/create it on the Mac with matching Folder ID.
  7. 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.