Syncthing: Continuous Sync With No Cloud in the Middle

Peer-to-peer file sync that never touches a server you don't own

Contents

I stopped trusting Dropbox the day I did the maths on what it actually costs to keep a few hundred gigabytes of photos and documents “in the cloud” forever, and realised I was renting a copy of my own data back from a company that could change the terms whenever it liked. What replaced it wasn’t another cloud service with a nicer privacy policy. It was Syncthing, a piece of software that doesn’t have a cloud at all. It just makes sure that whatever folder you point it at looks the same on every device you own, by having those devices talk to each other directly.

That distinction matters more than it sounds. Dropbox, Google Drive, and OneDrive are all fundamentally the same shape: your files live on someone else’s server, and every device syncs to that server. Syncthing has no server. Every device in your Syncthing cluster holds a full copy of the data, and devices exchange changes with their peers over an encrypted connection, with no third party ever holding a plaintext copy of your files. If you have a laptop, a phone, and a home server, the three of them just sync with each other. There’s no company between them, no monthly fee, and no storage cap beyond what your own disks provide.

Why Peer-to-Peer Beats a Hub

Advertisement

The “no cloud” bit is the headline, but the practical benefit is closer to home: your sync doesn’t stop working when your internet does. If your laptop and your home server are both on the same LAN, Syncthing discovers that and syncs directly at LAN speed, no internet round-trip required. Plug your laptop back into the network after a week away and it catches up in seconds rather than crawling through an upload cap.

Discovery is the part people find hardest to picture, since there’s no central address book. Syncthing solves it with two mechanisms. Local discovery broadcasts on the LAN so devices on the same network find each other automatically. Global discovery uses a small set of community-run (or self-hosted, if you’re inclined) discovery servers that devices announce themselves to, so a laptop on a coffee-shop network can still find your home server. Crucially, the discovery servers only ever learn that a device with a given ID exists at some IP address — they never see file contents, filenames, or even folder names. Every connection between devices is authenticated with device IDs derived from a public key and encrypted with TLS, so even the discovery infrastructure can’t read your traffic.

When two devices can’t reach each other directly — both behind restrictive NAT, no port forwarding, that sort of thing — Syncthing falls back to relaying through a community relay server. The relay only ever sees encrypted bytes it’s incapable of decrypting, forwarded on the fly rather than stored anywhere. You can run your own relay if you want to remove even that leap of faith, though for a home setup with Tailscale or a WireGuard tunnel already in place, you can usually just force devices to talk over that tunnel and skip relaying entirely.

Folder Types: Send/Receive, Send-Only, Receive-Only

Syncthing’s folder types are where it earns its keep over a naive two-way sync. Each device with a folder shared to it picks a type:

  • Send & Receive is the default: changes flow both ways, and this device can create, edit, and delete files that propagate to everyone else.
  • Send Only treats this device as the source of truth. It pushes its changes out but ignores changes coming from other devices. Handy for a folder of camera-import photos on a phone where you never want a peer accidentally deleting the originals.
  • Receive Only is the mirror image: this device accepts changes from peers but any local edits stay local and get flagged as “receive-only changes” rather than propagated, so you can inspect and discard them.

I run my archive folder as Send & Receive between my desktop and a home server, but Receive Only on a backup box that lives at a relative’s house over a Tailscale link — that machine should never be a source of edits, only a passive copy that gets whatever the primary devices agree on.

Versioning: Your Safety Net

Advertisement

Real-time sync has an obvious sharp edge: if you delete or overwrite a file on one device, that change propagates everywhere, including to your “backup” copy. Syncthing addresses this with per-folder versioning, configured independently of the sync itself.

Simple File Versioning keeps deleted or overwritten files in a .stversions directory inside the folder, for a configurable number of days. Staggered File Versioning keeps more recent versions densely and thins older ones out (an hour ago, a day ago, a week ago), which is a sane default for anything you’d actually miss. Trash Can Versioning just keeps one prior version of anything deleted, cheap and simple.

None of these make Syncthing a backup tool on their own — versioning lives on the same disks as the live data, so a dead drive still takes the version history with it. Pair it with an actual backup (I use Restic to a separate destination; see the comparison in Borg vs Restic) and treat Syncthing versioning purely as a “I fat-fingered a delete five minutes ago” undo button.

Setting It Up

Installation is a single static binary or a package from most distro repos. On a headless server, run it as a systemd user service:

1
2
3
4
5
# as the user that will own the synced files
systemctl --user enable --now syncthing.service

# the web UI binds to localhost:8384 by default — tunnel to it
ssh -L 8384:localhost:8384 [email protected]

The GUI defaults to listening only on 127.0.0.1, which is deliberate — Syncthing’s config UI has no built-in rate limiting on login attempts, so exposing it directly to the internet is asking for trouble. Reach it over SSH tunnel, Tailscale, or put it behind a reverse proxy with its own auth if you must expose it (see Reverse Proxy Done Right for the Caddy side of that).

Adding a device pairs by device ID, a 52-character string derived from that device’s certificate, shown in the GUI under Actions → Show ID. Paste it into the other device’s “Add Remote Device” dialog, accept the connection request on both ends, and share a folder to the new device. From then on, any change under that folder on any device propagates to every other device sharing it, continuously, with no scheduled sync window.

A minimal folder config, for reference (this lives in Syncthing’s own XML config, but the GUI is the sane way to edit it day to day):

1
2
3
4
5
6
7
8
<folder id="docs" label="Documents" path="/home/user/Documents" type="sendreceive">
    <device id="DEVICE-ID-OF-LAPTOP-XXXXXXX" />
    <device id="DEVICE-ID-OF-HOMESERVER-YYYYY" />
    <versioning type="staggered">
        <param key="cleanInterval" val="3600"/>
        <param key="maxAge" val="31536000"/>
    </versioning>
</folder>

Introducers and Bandwidth Limits

Once you’re past two or three devices, manually pairing every device to every other device gets tedious — a five-device mesh needs ten pairwise connections. Marking one device as an introducer lets the others learn about its known devices automatically: pair your phone and your home server directly, mark the home server as an introducer on your laptop, and the laptop picks up the phone’s device ID without you typing a 52-character string a third time. It’s purely a convenience — every device still has to individually accept the introduced connection before anything syncs.

Bandwidth limiting matters the moment one of your devices is on a metered connection. Syncthing lets you cap upload and download separately, globally or per-device, from Settings → Connections:

1
2
Maximum Send Rate (KiB/s): 500
Maximum Receive Rate (KiB/s): 0   # 0 = unlimited

I cap the laptop’s upload rate when it’s tethered to a phone hotspot, but leave the home-to-home link between my desktop and server uncapped, since both sit on the same gigabit LAN and there’s no reason to throttle a transfer that never leaves the house. Per-device overrides, set on the individual device rather than left at the global default, let you mix both policies in the same cluster without one slow link dragging down every other connection.

Ignore Patterns and Selective Sync

Not every device needs every file. A folder’s .stignore file works like .gitignore: patterns for files and directories to exclude from that device’s copy. My laptop ignores a RAW/ subdirectory inside my photos folder because I only need the JPEG exports on the go, while my home server syncs everything.

1
2
3
4
// .stignore in the photos folder, on the laptop only
RAW/
*.tmp
.DS_Store

Selective sync at the folder level (choosing which shared folders a given device actually wants) combines with .stignore at the file level to give you fairly fine control over what lives where, without maintaining separate folder structures per device.

Troubleshooting

Devices show as connected but nothing syncs. Check the folder is actually shared to both devices — adding a device to your cluster doesn’t automatically share every folder with it. Each folder has its own device list under Edit Folder → Sharing.

“Out of Sync” that never resolves. Usually a permissions problem: Syncthing can’t write the incoming file because the destination directory isn’t owned by the service user, or a file is locked open by another process. Check journalctl --user -u syncthing -f while triggering a sync for the specific I/O error.

Conflicts everywhere. If the same file gets edited on two devices before they can sync, Syncthing keeps both copies and renames the loser to filename.sync-conflict-<date>-<time>. This isn’t a bug, it’s Syncthing refusing to silently discard your work — but it does mean editing the same document simultaneously on two laptops is a bad habit to get into. For genuinely collaborative editing, reach for a tool actually built to merge concurrent edits.

High CPU on scan. Syncthing rescans folders periodically (default every hour) by walking the tree and hashing changed files. On a folder with hundreds of thousands of small files this can be noticeably heavy; raise rescanIntervalS or, better, enable filesystem watching (inotify on Linux) so it reacts to actual change events instead of polling.

NAT traversal just won’t work. If two devices never connect directly despite both having internet, confirm neither firewall is blocking UDP 22000 (used for local discovery and QUIC) and TCP 22000. Failing that, check the Syncthing GUI’s connection type for that device — if it says “relay”, direct connection failed and you’re paying a bandwidth and latency tax through the relay. Forcing both ends onto a WireGuard or Tailscale overlay (see The Tunnel Home) usually fixes this for good, since then they always have a direct route.

Self-Hosting Discovery and Relay

If leaning on community discovery and relay servers doesn’t sit well with you, both are self-hostable, and doing so removes the last piece of infrastructure you don’t control from the picture entirely. The discovery server (stdiscosrv) and relay server (strelaysrv) are separate small binaries from the same project:

1
2
3
4
5
6
# a minimal self-hosted discovery server, reachable over your WireGuard tunnel
stdiscosrv --cert=/etc/syncthing/discovery-cert.pem --key=/etc/syncthing/discovery-key.pem

# point every device at it instead of the default public pool
# in each device's Settings → Connections → Global Discovery Servers:
# https://discovery.mylab.local:8443/v2/

For a homelab where every device is already reachable over a Tailscale or WireGuard mesh, this is mostly belt-and-braces — you could equally just disable global discovery entirely and rely on the static IPs your overlay network already gives each device. I run a self-hosted discovery server anyway, mostly so a laptop away from home still finds its way back without me hardcoding an address that might change.

Verdict

Syncthing is a genuinely different thing from Dropbox or a backup tool: continuous, encrypted, peer-to-peer replication of a folder tree across however many machines you own. If your use case is “I want my notes/photos/documents to be the same everywhere without paying anyone rent for the privilege”, it’s close to unbeatable, and the versioning gives you enough of a safety net for everyday mistakes. Pair it with a real backup for anything irreplaceable, run it on a machine that’s rarely offline as your de facto hub for scheduling purposes, and it will quietly do its job for years without a subscription renewal email in sight.

Advertisement
Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.