Syncthing¶
Overview¶
Syncthing keeps folders in sync between devices, peer to peer, without a cloud in the middle. I use it for one
job: folders on my desktop (Documents, 3D-printing projects, ...) get an immediate copy on the NAS. That gives me
- a backup that's seconds old instead of last night's, with version history on the NAS side,
- the same files on the rarely used Windows install, and
- a NAS copy that the regular nightly backup (borgmatic) picks up like any other directory.
Design: hub and spoke¶
workstation (Linux) ──┐
├──► nas (always-on Syncthing container, the hub) ──► nightly backup
workstation (Windows) ┘
- The NAS is the hub: a Syncthing container that is always on. Every desktop client (spoke) shares its folders with the NAS only, not with each other. When one machine is off, the others still sync through the hub, and there's always one complete copy in a known place.
- Versioning lives on the hub. Each folder on the NAS uses staggered versioning with a 30-day max age, stored in
.stversionsinside the folder. A delete or overwrite on a client propagates, but the old version stays recoverable on the NAS for a month. Clients don't need versioning of their own. - LAN only. Global discovery, relays, NAT traversal (UPnP/NAT-PMP) and usage reporting are switched off on every
device. Devices are added by ID with an explicit address like
tcp://<YOUR_SERVER_IP>:22000, because local discovery uses broadcasts, which don't cross VLANs. If you segment your network, allow22000/tcpfrom the client VLAN to the NAS. - Folder layout on the NAS: one directory per folder ID,
/mnt/storage/sync/<folder-id>, owned by the same user as the clients. Config, keys and the index database live on the SSD (/mnt/appdata/syncthing), not the pool.
| Folder ID | Workstation | NAS | Type |
|---|---|---|---|
3d_printing |
~/Documents/3d_printing |
/mnt/storage/sync/3d_printing |
Send & Receive |
<folder-id> |
any folder you add (see the Dolphin menu below) | /mnt/storage/sync/<folder-id> |
Send & Receive |
NAS (hub) compose¶
services:
syncthing:
image: syncthing/syncthing:2.1.6
container_name: syncthing
hostname: <NAS_HOSTNAME> # the device name other peers see
restart: unless-stopped
# Runs directly as your user instead of the image's PUID/PGID su-exec step, which would need CHOWN/SETUID/SETGID.
user: "<PUID>:<PGID>"
environment:
- STGUIADDRESS=0.0.0.0:8384
volumes:
# Config, keys and index DB on the SSD, not the pool.
- /mnt/appdata/syncthing:/var/syncthing
- /mnt/storage/sync:/data
ports:
- "<YOUR_SERVER_IP>:8384:8384" # GUI on the LAN address only
- "22000:22000/tcp"
- "22000:22000/udp"
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
mem_limit: 1g
# Same command as the image's own HEALTHCHECK; /rest/noauth/health needs no API key.
healthcheck:
test: ["CMD-SHELL", "curl -fkLsS -m 2 127.0.0.1:8384/rest/noauth/health | grep -o --color=never OK || exit 1"]
interval: 1m
timeout: 10s
retries: 3
start_period: 30s
No .env is needed. After the first start:
- Open
http://<YOUR_SERVER_IP>:8384→ Actions > Settings > GUI: set a GUI user and password right away (anyone on the LAN can otherwise change your sync config). Store the password in your password manager. - Settings > Connections: untick Global Discovery, Enable Relaying, Enable NAT traversal. Decline usage reporting.
- Settings > General: set the default folder path to
/data, so new folders land in the right place.
user: + cap_drop: ALL works because the container never needs to change ownership: it runs as the same UID that
owns /mnt/storage/sync, matching the clients' files. If you pause containers during a SnapRAID sync, include this
one; clients just queue their changes until it's back.
Workstation (spoke) on Debian¶
Debian 13's own syncthing package is still 1.x; the official apt repo has 2.x:
sudo mkdir -p /etc/apt/keyrings
sudo curl -L -o /etc/apt/keyrings/syncthing-archive-keyring.gpg https://syncthing.net/release-key.gpg
echo "deb [signed-by=/etc/apt/keyrings/syncthing-archive-keyring.gpg] https://apt.syncthing.net/ syncthing stable-v2" \
| sudo tee /etc/apt/sources.list.d/syncthing.list
sudo apt update && sudo apt install syncthing
Run it as a user service, so it runs as you and starts at boot:
systemctl --user enable --now syncthing.service
sudo loginctl enable-linger $USER # keep it running without a desktop login
- GUI on
http://127.0.0.1:8384(loopback only), config + DB in~/.local/state/syncthing/. - Updates come through
apt upgrade(Syncthing's built-in auto-upgrade is off for packaged installs). unattended-upgrades only takes Debian origins by default, so this repo updates when you run apt yourself. - Same Connections settings as the hub (no global discovery/relays/NAT), then Add Remote Device: the NAS device
ID, and under Advanced > Addresses
tcp://<YOUR_SERVER_IP>:22000. Accept the device on the NAS side. - For a tray icon and a Dolphin "Syncthing" submenu with status/rescan, install Syncthing Tray
(
sudo apt install syncthingtray-kde-plasma). In its first-run wizard, point it at the existing systemd user service instead of letting it start its own Syncthing.
Windows: install Syncthing 2.x (official installer or Syncthing Tray), apply the same Connections settings, add the NAS by ID + address, and share folders with the NAS, not straight with the Linux desktop, so the NAS stays the hub.
Desktop integration (KDE Dolphin)¶
Adding a folder by hand means clicking through two GUIs (create on the NAS with versioning, share from the client). I turned that into a right-click.
"Add to Syncthing (NAS)" service menu¶
A KIO service menu calls a small script with the selected folder(s):
[Desktop Entry]
Type=Service
MimeType=inode/directory;
Actions=syncthingAddFolder;
X-KDE-Priority=TopLevel
[Desktop Action syncthingAddFolder]
Name=Add to Syncthing (NAS)
Icon=syncthing
Exec=/home/<YOUR_USER>/.local/bin/syncthing-add-folder %F
Plasma 6 only loads service menus that are executable (chmod 755 the .desktop file), and the session's
PATH doesn't necessarily include ~/.local/bin, hence the absolute Exec path.
The script (Python, standard library only) does, per folder:
- Asks for a folder ID with
kdialog(default: the folder name), showing the folder size and the NAS's free space. - Creates the folder on the NAS first over the REST API, path
/data/<id>, shared with this workstation, with 30-day staggered versioning. If that fails, nothing is half-shared. - Adds the folder locally, shared with the NAS.
- Reports the result with
notify-send(Dolphin hides stderr, so errors need a notification too).
Both ends are just PUT /rest/config/folders/<id> calls with the X-API-Key header. The NAS side, for example:
curl -X PUT -H "X-API-Key: <YOUR_API_KEY>" -H "Content-Type: application/json" \
http://<YOUR_SERVER_IP>:8384/rest/config/folders/3d_printing -d '{
"id": "3d_printing", "label": "3d_printing", "path": "/data/3d_printing", "type": "sendreceive",
"devices": [{"deviceID": "<NAS_DEVICE_ID>"}, {"deviceID": "<WORKSTATION_DEVICE_ID>"}],
"versioning": {"type": "staggered", "params": {"maxAge": "2592000"}, "cleanupIntervalS": 3600}
}'
A few rules make it safe:
- Syncthing doesn't support overlapping folders. Adding a parent of already synced folders (e.g.
DocumentswhenDocuments/3d_printingis synced) merges them: the child folders are removed as separate Syncthing folders, and their NAS copy (with.stversions) is moved into place inside the new folder on the NAS, so nothing is re-uploaded. - A folder inside an already synced one is already covered, so the script just says so.
/and~itself are refused. - The NAS API key is read over SSH on each run instead of being copied to the workstation.
- To stop syncing, remove the folder in both GUIs; the NAS copy stays until you delete it.
Status badges (overlay icons)¶
The second half: seeing at a glance whether a folder is safely on the NAS. Dolphin supports overlay icon plugins
(KOverlayIconPlugin, the same mechanism Dropbox/Nextcloud clients use). A small C++ plugin badges every directory
inside a synced folder:
| Badge | Meaning |
|---|---|
| green check | NAS connected, folder idle, NAS completion 100 % |
| blue arrow | scanning/syncing, or the NAS still needs data |
| orange ! | NAS not connected, folder error/paused, or local Syncthing down |
How it works:
- It polls the local REST API every few seconds (
/rest/system/connections,/rest/db/status,/rest/db/completion?folder=<id>&device=<NAS_DEVICE_ID>) and caches the result; the overlay callback must never block Dolphin. - Folders, API key, GUI address and the NAS device ID come from the local
config.xml. It watches the config directory (Syncthing replacesconfig.xmlatomically, which drops a watch on the file itself), so folders added by the script get badges without restarting Dolphin. - Only directories get badges, not files, so big folders stay readable.
- Its own small round emblem icons; Breeze's square check emblem looks like the selection checkbox.
- Build needs
libkf6kio-dev; the.sogoes into Qt'splugins/kf6/overlayicon/directory outside dpkg, so rebuild it after a KDE Frameworks/Qt major upgrade. Restart Dolphin afterwards.
Backups¶
The NAS copy is a source in the nightly backup (/mnt/storage/sync), so synced folders get the regular off-box and
offsite copies on top of Syncthing's 30-day version history. Syncthing alone is not a backup: a bad change syncs
everywhere in seconds; the versioning and the nightly archives are what let you go back.