New releases
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Editorial pickFile synchronisation
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.
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.
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.
These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Result of a dedicated Docker image scan, not a Compose configuration check.
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.
Upstream Compose changes relative to the verified recipe.
Pinned image tags checked for newer versions.
From observation to action
The recipe already pins the latest known release. The steps below show the evidence retained for that version.
v2.1.3v2.1.3Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.
The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.
No image-tag change is currently planned.
Change the tag and restart. Release-specific migration steps still come from the release notes and installation guide.
No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.
An exact Fully verified check for this target was recorded on 2026-08-31.
The status shows how fully the published recipe meets each level's requirements.
A minimal setup for a local network or access through a VPN.
The recipe covers every requirement at this level.
A public deployment with protected access and backups.
The recipe covers every requirement at this level.
A setup designed for predictable operations and observability.
You still need to add: separate database, external monitoring, cpu and memory limits.
Tailored to your server
Answer five questions and download a ready-to-run Syncthing bundle.
Screenshots have not been added yet.
Read the full guide first: it explains external access, backups, and data locations.
cp .env.example .envdocker compose up -ddocker compose psThis 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
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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 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 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.
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.
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.
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.