All articles
TutorialsJun 09, 2026 · 24 min read

Self-Host Syncthing on a VPS for Continuous File Sync

Self-Host Syncthing on a VPS for Continuous File Sync

Syncthing keeps folders in sync across your machines without a cloud account, a subscription, or anyone else's servers touching your files. It's peer-to-peer: your laptop talks to your desktop talks to your phone, encrypted end to end. The one weakness of a pure peer-to-peer setup is obvious the moment you think about it - two devices can only sync when they're both online at the same time. Close the laptop before the desktop wakes up and the changes just wait.

A VPS fixes that. Park a Syncthing node on a server that never sleeps and it becomes the always-on hub every other device syncs against. Your phone uploads a photo, the VPS has it seconds later, and your desktop pulls it down whenever it next comes online - no overlap required. The same node doubles as an off-site backup of every folder you point at it.

This guide sets up Syncthing on a single VPS with Docker, puts the admin GUI behind Caddy with automatic HTTPS, and walks through pairing devices, one-way folders, file versioning, and the encrypted "untrusted node" trick that lets you store data on a server without trusting the server with it.

TL;DR

  • Install Docker and run Syncthing from one docker-compose.yml
  • Publish only the sync port (22000 TCP and UDP); keep the GUI internal
  • Put the web GUI behind Caddy at sync.example.com with auto-HTTPS and a password
  • Pair each device by its device ID, then share folders between them
  • Use receive-only on the VPS so it acts as a pure backup target
  • Turn on file versioning so deletes and overwrites are recoverable
  • Optionally mark the folder untrusted so the VPS stores only encrypted blocks

Total time: about 25 minutes.

What You Need

  • A VPS with 1 GB RAM running Ubuntu 22.04 or 24.04 (512 MB works for small folders)
  • A domain you can add DNS records to, for the GUI
  • Ports 80 and 443 open for Caddy, plus 22000 TCP/UDP for sync traffic
  • Root or sudo access
  • Syncthing already installed (or ready to install) on at least one other device

Syncthing is light on CPU and RAM but it scans files, so give it disk headroom: the data you sync plus a little room for the version history you'll enable later.

Step 1: Point a Subdomain at Your VPS

In your DNS provider, add an A record for the GUI:

sync.example.com → YOUR_VPS_IPV4

Add an AAAA record too if you have IPv6. Confirm it resolves before going further:

dig +short sync.example.com

The sync traffic itself does not need DNS - devices find each other by ID through Syncthing's global discovery. The subdomain is only for reaching the admin interface.

Step 2: Install Docker and Docker Compose

On a fresh Ubuntu box:

sudo apt update sudo apt install -y ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \ https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verify:

docker --version docker compose version

Step 3: Open the Firewall

Syncthing needs two things open from the outside: Caddy's web ports for the GUI, and the sync transport port.

sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw allow 22000/tcp sudo ufw allow 22000/udp sudo ufw enable

Port 22000/tcp carries the main sync stream and 22000/udp carries QUIC, which Syncthing prefers when it can. You do not need to open 21027/udp - that's local LAN discovery and is useless across the internet. You also do not need 8384 open; the GUI stays behind Caddy.

Step 4: Create the Project Directory

sudo mkdir -p /opt/syncthing cd /opt/syncthing sudo mkdir -p config data caddy-data caddy-config
  • config/ - Syncthing's identity, keys, and folder config (the part you can't lose)
  • data/ - the actual synced files
  • caddy-* - Caddy's certificate and state

Step 5: Write the Compose File

Create /opt/syncthing/docker-compose.yml:

services: syncthing: image: syncthing/syncthing:latest container_name: syncthing hostname: vps-node restart: unless-stopped environment: PUID: "1000" PGID: "1000" volumes: - ./config:/var/syncthing/config - ./data:/var/syncthing/data ports: - "22000:22000/tcp" - "22000:22000/udp" networks: - sync-net caddy: image: caddy:2 container_name: syncthing-caddy restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - ./caddy-data:/data - ./caddy-config:/config networks: - sync-net networks: sync-net:

The GUI port 8384 is intentionally not published to the host. Caddy reaches it over the internal sync-net network, so the only way in from the internet is through HTTPS with a password.

hostname: vps-node is what shows up as this device's name in the GUI - pick something you'll recognize.

Step 6: Write the Caddyfile

Create /opt/syncthing/Caddyfile:

sync.example.com { reverse_proxy syncthing:8384 }

Caddy requests a Let's Encrypt certificate on first boot and renews it forever. Syncthing's GUI is a long-lived single-page app with an event stream, and Caddy proxies that without any extra tuning. If you want the full reverse-proxy pattern, see our Caddy guide.

Step 7: Start the Stack

cd /opt/syncthing sudo docker compose up -d sudo docker compose logs -f

Watch the logs until Caddy reports a certificate and Syncthing prints My ID: .... That long string is this node's device ID - the public key fingerprint other devices use to find and trust it. Copy it; you'll need it in a moment. You can always read it back later:

sudo docker exec syncthing syncthing --device-id

Step 8: Lock Down the GUI

Open https://sync.example.com. The web interface loads, but right now it has no password - anyone who finds the URL can reconfigure your sync. Fix that immediately.

In the GUI, go to Actions -> Settings -> GUI and set:

  • GUI Authentication User and GUI Authentication Password - any admin login you like.
  • Leave Use HTTPS for GUI off. Caddy already terminates TLS in front, and double-encrypting just trips the host check.

Save. The page reloads and asks for the credentials.

Set the GUI password before you do anything else. An open Syncthing GUI is a remote file-management console for every folder the node touches - someone who reaches it can add their own device, share your folders out, or delete them. The container does not publish port 8384 to the host, so Caddy plus this password is the only gate. Don't skip it "for now."

If the GUI shows a host check error through the proxy, add this under the GUI settings or set it once via the config: tell Syncthing to skip the host header check, since Caddy is the real front door. The cleanest way is the environment variable - add STGUIADDRESS: "0.0.0.0:8384" to the compose environment block and recreate the container.

Step 9: Pair Your First Device

Syncthing pairing is mutual: each side adds the other's device ID, and both must accept. Say you're connecting your laptop.

On your laptop, open its Syncthing GUI and copy its device ID from Actions -> Show ID.

On the VPS GUI, click Add Remote Device, paste the laptop's ID, give it a name like laptop, and save.

Back on the laptop, a prompt appears asking whether to add the new device that just contacted it (the VPS). Accept it, name it vps-node, and save.

Within a few seconds both GUIs should show the other device as Connected. If they sit on "Disconnected" for more than a minute, jump to Troubleshooting - it's almost always the sync port.

Step 10: Share a Folder

Pairing connects devices; sharing connects folders. On the device that has the data - say the laptop holds ~/Documents - do this:

  1. In the laptop GUI, Add Folder (or edit an existing one).
  2. Give it a Folder Label (Documents) and note the Folder ID - it must match on every device that shares it.
  3. On the Sharing tab, tick vps-node. Save.

On the VPS GUI a notification appears: the laptop wants to share Documents. Accept it. Set the folder path on the VPS to something under the synced data volume, for example /var/syncthing/data/Documents, and save.

The first sync scans and transfers everything. After that, only changed blocks move, so subsequent syncs are near-instant.

Step 11: Make the VPS a Pure Backup Target

By default folders are bidirectional - a change anywhere propagates everywhere, including deletes. That's perfect for working files but risky for a backup node: delete a file on your laptop by accident and the VPS dutifully deletes its copy too.

To make the VPS hold everything and never push changes back, set its copy of the folder to Receive Only:

  1. On the VPS GUI, edit the shared folder.
  2. Under Advanced -> Folder Type, choose Receive Only.
  3. Save.

Now the VPS accepts incoming changes but never sends its own, and if something does diverge it surfaces a "Revert Local Changes" button instead of silently overwriting. Pair this with versioning in the next step and the VPS becomes a genuine safety net, not just a mirror.

The mirror image is Send Only, useful when the VPS is the source of truth - say it generates reports other machines should pull but never modify.

Step 12: Turn On File Versioning

Versioning is what turns sync into something you can recover from. Without it, an overwrite or delete that syncs is gone everywhere. With it, the VPS keeps old copies.

On the VPS copy of the folder, edit it and open File Versioning. Pick one:

  • Trash Can - keeps the most recent deleted/replaced version for N days. Simple and usually enough.
  • Staggered - keeps versions with decreasing density over time (hourly for a day, daily for a month, and so on), capped by a max age. Best for an important backup node.

For a backup target, Staggered with a max age of 90 days is a sensible default. Versions live in a .stversions folder inside the synced directory, so they count against disk - budget for it.

Step 13: Store Data Without Trusting the Server

This is the trick that makes a VPS a great Syncthing target even if you don't fully trust the host: untrusted (encrypted) devices. When you mark a remote device as untrusted for a folder, your other devices encrypt the data with a password before sending it. The VPS stores only ciphertext - it can sync the blocks but cannot read a single file.

To enable it, on a trusted device (your laptop), edit the folder, open the Sharing tab, and next to vps-node set an encryption password. Every trusted device sharing that folder must use the exact same password. The VPS itself is never given the password; it just relays and stores encrypted blocks.

The encryption password is unrecoverable. Syncthing cannot reset it, and the data on the untrusted node is useless without it. Store it in a password manager the moment you set it - if you self-host one, our [Vaultwarden guide](/blog/self-host-vaultwarden-on-a-vps-with-docker) covers exactly that. Lose the password and you lose access to anything that only existed on the encrypted node.

Untrusted mode is ideal for a cheap or shared VPS, or any server you'd rather not have plaintext on. The cost is that the VPS GUI can no longer show file names or restore versions in the clear - recovery happens on a trusted device.

Step 14: Back Up the Configuration

The synced files protect themselves by being on multiple devices. The one thing that's unique to this node is /opt/syncthing/config - it holds the device identity and keys. Lose it and the VPS gets a new device ID, which means re-pairing everything.

Create /usr/local/bin/syncthing-backup.sh:

#!/usr/bin/env bash set -euo pipefail BACKUP_DIR="/var/backups/syncthing" DATE="$(date +%F)" mkdir -p "$BACKUP_DIR" tar -czf "$BACKUP_DIR/syncthing-config-$DATE.tar.gz" -C /opt/syncthing config find "$BACKUP_DIR" -name "syncthing-config-*.tar.gz" -mtime +14 -delete

Make it executable and schedule it:

sudo chmod +x /usr/local/bin/syncthing-backup.sh echo "15 4 * * * root /usr/local/bin/syncthing-backup.sh" | \ sudo tee /etc/cron.d/syncthing-backup

For off-site copies of the actual data, point restic at the data directory - Syncthing handles live sync, restic handles cold, immutable snapshots.

Step 15: Upgrade Safely

Syncthing upgrades are uneventful:

cd /opt/syncthing sudo docker compose pull sudo docker compose up -d

Take a config backup first. Because the config is a plain bind mount, a bad upgrade is a docker compose down, restore the tarball, pin the previous image tag, and up -d.

Troubleshooting

Devices stay "Disconnected." The sync port isn't reachable. Confirm 22000/tcp and 22000/udp are open in UFW and in your provider's external firewall, then check the Listeners in the GUI under Actions -> Status show as online rather than LISTEN_ERROR.

GUI shows a "Host check error." Syncthing is rejecting the proxied Host header. Set STGUIADDRESS: "0.0.0.0:8384" in the compose environment block and recreate the container, or disable the GUI host check in settings.

Caddy can't get a certificate. DNS hasn't propagated, or ports 80/443 are blocked upstream. Verify with dig and check your provider's firewall panel.

A folder is stuck at "Out of Sync." Usually a permissions mismatch - the container's PUID/PGID don't own the data directory. Run sudo chown -R 1000:1000 /opt/syncthing/data and rescan.

Sync is slow over the internet. If devices fall back to a relay, throughput drops. Make sure at least one side has the sync port directly reachable; the VPS is the natural candidate since it has a public IP and an open 22000.

Going Further

  • Set rescan and watch intervals. Enable filesystem watching per folder for near-instant detection instead of waiting on the periodic scan.
  • Use ignore patterns. A .stignore file in a folder keeps node_modules, build artifacts, and caches out of sync to save bandwidth and disk.
  • Run it private. If the GUI never needs to face the public internet, drop the Caddy subdomain and reach it over a VPN with our Tailscale guide.
  • Monitor the node. Point Uptime Kuma at the GUI URL so you hear about it the moment the hub goes offline.
  • Harden the host. A node holding copies of all your files deserves a locked-down box - see our SSH hardening and Fail2Ban guide.

That's the whole setup. One always-on VPS turns Syncthing from "sync when two devices happen to overlap" into a real hub-and-spoke system, with versioned history and optional zero-knowledge encryption, and not a cloud subscription in sight.


Need an always-on box to anchor your sync setup? Our Linux plans include fast NVMe storage, IPv6, and generous bandwidth - ideal for a Syncthing hub that's online around the clock. See the options.