Fully verified88,148

Editorial pickFile synchronisation

Syncthing

Syncthing keeps chosen folders identical on several devices and transfers files directly between them over an encrypted channel. There is no server in the middle: every node holds a copy, and the server in this recipe acts as the always-available participant in the exchange.

Overview

Syncthing synchronises folders between your devices directly. A laptop, a phone, and a server negotiate among themselves: files travel over an encrypted channel from device to device, and there is simply no intermediate service holding a copy. It fits anyone who wants the familiar “the same folder on every device” without handing the contents to someone else’s cloud.

The server in this recipe is not storage in the cloud sense but an always-running participant in the exchange. A phone and a laptop are often off and rarely see each other; a node on the server is always reachable, so changes propagate faster and devices never have to be online at the same time.

The recipe runs a single container. The configuration, device keys, and index database live in a Docker volume, and the synchronised files in a host directory. The reliable level is not claimed: Syncthing has no separate database, and the recipe adds no monitoring and no resource limits.

Syncthing is not a backup. A deletion propagates to every device as fast as any other change. Enable File Versioning for folders where accidental deletion matters, and keep separate backups.

Security assessment

The container runs without privileged, host networking, or Docker socket access, uses no-new-privileges, and runs as the unprivileged user 1000:1000. Traffic between nodes is TLS encrypted, and devices identify each other by cryptographic identifiers that must be confirmed by hand on both sides.

Syncthing’s network model differs from the other apps in this catalogue: sync port 22000 is published on all interfaces, because without it direct connections never form and the exchange falls back to public relays. The web interface still stays on 127.0.0.1 and is published only through a reverse proxy. It has no password by default — set one before opening any access from outside.

The configuration archive contains the device’s private key: anyone who obtains it can impersonate your node inside the cluster. Encrypt the copies that leave the server.

The recipe image is scanned with Trivy and has no critical findings — it is Alpine plus a single statically linked Go binary.

Update tracking

These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.

New releases

Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.

Current version v2.1.3Aug 31, 2026, 8:59 AM UTC

Critical vulnerabilities

Result of a dedicated Docker image scan, not a Compose configuration check.

No critical vulnerabilities foundAug 31, 2026, 9:07 AM UTC

Breaking updates

A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.

Not checkedNot checked

compose.yaml

Upstream Compose changes relative to the verified recipe.

Upstream Compose is not trackedNot checked

Outdated Docker images

Pinned image tags checked for newer versions.

All images are currentAug 31, 2026, 9:07 AM UTC

From observation to action

Update plan

The recipe already pins the latest known release. The steps below show the evidence retained for that version.

v2.1.3v2.1.3
  1. Open the release evidence

    Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.

  2. Review the version boundary

    The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.

  3. Prepare image-tag changes

    No image-tag change is currently planned.

  4. Follow the documented migration shape

    Change the tag and restart. Release-specific migration steps still come from the release notes and installation guide.

  5. Protect the rollback point

    No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.

  6. Verify the updated recipe

    An exact Fully verified check for this target was recorded on 2026-08-31.

Read the update procedure in the installation guide ↓

Specifications

Security and exposure

Installation levels

The status shows how fully the published recipe meets each level's requirements.

Ready

For home

A minimal setup for a local network or access through a VPN.

  • Single server
  • Local access
  • Minimum components

The recipe covers every requirement at this level.

Ready

For a VPS

A public deployment with protected access and backups.

  • Domain
  • HTTPS via reverse proxy
  • Off-server backup

The recipe covers every requirement at this level.

Not supported

Reliable

A setup designed for predictable operations and observability.

  • Separate database
  • Healthcheck
  • External monitoring
  • CPU and memory limits

You still need to add: separate database, external monitoring, cpu and memory limits.

Tailored to your server

Configuration generator

Answer five questions and download a ready-to-run Syncthing bundle.

Runs locally
01 Do you have a domain?
02 Which reverse proxy?
03 Server architecture
04 Where should data be stored?

Use an absolute path for a separate disk, such as /srv/syncthing.

05 Is VPN access required?

Your bundle is ready

The archive is created in your browser. Your answers are never sent anywhere.

Screenshots

Screenshots have not been added yet.

Quick start

Read the full guide first: it explains external access, backups, and data locations.

Prepare settingscp .env.example .env
Launchdocker compose up -d
Check statusdocker compose ps

Step-by-step guide

1. Check your Ubuntu or Debian server

This recipe needs 1 CPU core, 256 MB of RAM, and 2 GB of disk for the node itself; 512 MB of RAM is recommended. This is a conservative estimate for the recipe: upstream publishes no formal minimum, and memory use grows with the number of files in the index. Count storage for the files separately: Syncthing keeps a full copy of every synchronised folder. You need Ubuntu 22.04+ or Debian 12+ with Docker Engine and Docker Compose v2.24+.

docker --version
docker compose version

2. Prepare the files and variables

mkdir -p ~/services/syncthing
cd ~/services/syncthing
cp .env.example .env
chmod 600 .env
sudo mkdir -p /srv/syncthing
sudo chown 1000:1000 /srv/syncthing
sed -i "s|^SYNCTHING_DATA_LOCATION=.*|SYNCTHING_DATA_LOCATION=/srv/syncthing|" .env

SYNCTHING_VERSION pins the image; SYNCTHING_DEVICE_NAME is the name other nodes see; SYNCTHING_GUI_PORT publishes the web interface on localhost only; SYNCTHING_SYNC_PORT and SYNCTHING_DISCOVERY_PORT carry the data exchange and local discovery; SYNCTHING_DATA_LOCATION is the directory with the synchronised files; SYNCTHING_CONFIG_VOLUME is the volume with the configuration, device keys, and index database; SYNCTHING_UID and SYNCTHING_GID set the file owner; TZ sets the time zone.

Directory ownership matters: the container runs as 1000:1000, and without the chown Syncthing cannot write to /srv/syncthing.

3. Start the node and set a password

docker compose pull
docker compose up -d
docker compose ps
ssh -L 8384:127.0.0.1:8384 user@server.example

Open http://localhost:8384. The web interface has no password at first, so go to Actions → Settings → GUI right away and set a user and password — the interface must not be published without one.

The device ID (Actions → Show ID) is what nodes exchange when they meet. On the second device add the server by that ID, accept the request on the server, then pick a folder and choose which device to share it with.

Running on a VPS

On a VPS the web interface stays on 127.0.0.1, but the sync port must be reachable from outside — otherwise devices connect only through public relays and throughput drops several times over. Open it in the firewall:

sudo ufw allow 22000/tcp
sudo ufw allow 22000/udp
docker compose up -d
docker compose ps
curl --fail http://127.0.0.1:8384/rest/noauth/health

Port 21027/udp is only for discovering devices on the same local network; there is no reason to open it on a VPS.

Access from a local network

Inside a trusted LAN the web interface is most conveniently reached through an SSH tunnel or a VPN. If the reverse proxy runs on another host in the network, replace 127.0.0.1 in compose.yaml with the server’s LAN address and restrict the port to the proxy address in the firewall.

Port 21027/udp enables automatic discovery: devices on the same network find each other without typing addresses. The data exchange still goes over port 22000.

Domain and HTTPS

Only the web interface goes through the reverse proxy. The sync port speaks its own protocol and does not pass through an HTTP proxy: it stays on 22000.

Replace sync.example.com with your single domain in the file you pick from proxy/. proxy/Caddyfile obtains a certificate automatically; proxy/nginx.conf expects a Certbot certificate; proxy/traefik.yaml uses the letsencrypt resolver. For Traefik in a container, replace 127.0.0.1 with a host gateway it can reach.

Set a password under Settings → GUI before exposing the interface. The Nginx sample raises proxy_read_timeout: the interface holds long event requests, and a short timeout breaks the connection.

curl --fail https://sync.example.com/rest/noauth/health

Backup

chmod +x backup.sh restore.sh
./backup.sh

The script stops the container, archives the configuration volume, and starts it again. The archive holds the device’s private key, its identifier, the folder and device lists, and the index database.

The synchronised files are not in the archive, deliberately: every node already holds a copy. What matters more is this: Syncthing is not a backup. A deletion propagates to every device as fast as any other change. Enable File Versioning in the folder settings to survive an accidental deletion, and use a dedicated tool for real backups.

The archive contains a private key: encrypt it before it leaves the server.

Restore

Restoring brings back the previous device identifier, so the other nodes keep working without being re-added:

./restore.sh ./backups/syncthing-YYYYMMDDTHHMMSSZ.tar.gz
docker compose ps
curl --fail http://127.0.0.1:8384/rest/noauth/health

Before replacing anything the script takes a safety copy of the current configuration. If the data directory is empty, the node downloads the files again from the other devices; if the files are in place, Syncthing rescans them and finds the match.

Never run two copies of the same configuration at once: two nodes with the same identifier conflict inside the cluster.

Update

Take a backup and read the release notes. Change only the pinned SYNCTHING_VERSION, then run:

./backup.sh
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 syncthing

Nodes of different versions work together, but protocol compatibility is not unlimited: across major versions, update all devices at roughly the same time.

Rollback

The index database format changes between major versions and does not migrate downwards. Restore the pinned SYNCTHING_VERSION in .env and restore the archive taken before the update:

docker compose pull
./restore.sh ./backups/syncthing-before-update.tar.gz
docker compose up -d

The files themselves are not at risk: at worst the node rebuilds its index.

Stopping and complete removal

docker compose down removes the container but keeps the configuration and keys. Complete, irreversible removal after verifying a backup:

docker compose down
docker volume rm syncthing-config
rm -rf ~/services/syncthing

The SYNCTHING_DATA_LOCATION directory stays where it is: the recipe never deletes it. After the node is gone, the other devices show it as disconnected until you remove it from their settings.

Sources: getting started, configuration, firewall and ports, file versioning, reverse proxy, and security.

Troubleshooting

Devices cannot find each other

Check that the sync port is open from outside and that the node listens on it:

docker compose ps
curl --fail --silent http://127.0.0.1:8384/rest/noauth/health
sudo ufw status | grep 22000

In the web interface the Listeners row should show every address as active. If the connection still goes through a relay, the device card shows Connection Type Relay instead of TCP LAN or TCP WAN — the direct connection failed and port 22000 is closed on at least one side.

Permission error on the folder

The container runs as 1000:1000, and a host directory often belongs to root:

ls -ld /srv/syncthing
sudo chown -R 1000:1000 /srv/syncthing

SYNCTHING_UID and SYNCTHING_GID set a different owner when the server needs one.

A file was deleted on every device

That is expected: Syncthing synchronises deletions. It can be recovered from versions if File Versioning is enabled for the folder — expand the folder card and press Versions. Without versioning, only an external backup helps.

A folder is stuck Out of Sync

Usually an edit conflict or an unreadable file:

docker compose logs --tail=200 syncthing | grep -i "error\|conflict\|permission"

Syncthing keeps conflicting copies next to the file, named like name.sync-conflict-DATE-TIME.extension. Pick the version you want and delete the other.

The web interface rejects the password

The password lives in the configuration inside the volume. Reset it by editing config.xml while the container is stopped:

docker compose stop syncthing
docker run --rm -it -v syncthing-config:/c alpine:3.22 \
  sh -c 'sed -i "s@<password>.*</password>@<password></password>@" /c/config/config.xml'
docker compose start syncthing

The interface then opens without a password — set a new one immediately.

The node reports a different identifier after a move

The device identifier is tied to the private key in the volume. If you copied the configuration to a second server and started both, the cluster now has two nodes with the same ID, which is not allowed. Keep one copy and create a new node on the other machine.

High memory use

Memory grows with the number of files in the index. Excluding unneeded directories through Ignore Patterns in the folder settings and increasing the scan interval both help.

The node does not start after an update

docker compose logs --tail=200 syncthing | grep -i "error\|migrat"

The index database format does not migrate downwards: the only way back to the previous version is restoring the archive taken before the update. The files themselves are not affected.

Official sources

Latest release: 2026-08-05 · GitHub Stars: 88,148 · metadata checked Aug 31, 2026, 8:59 AM UTC

Disclaimer

Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.

A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.