Syncthing + Tailscale + Synology DSM

Following the trend of self-hosting more software in response to the instability of today’s tech industry, I thought of trying Syncthing, “an application that lets you synchronize your files across multiple devices”.

I want to run Syncthing in my Tailscale tailnet and for the occasion I thought of trying to run Docker on my Synology NAS, which is something I haven’t tried before; the alternative would have been to run Syncthing in one of my homelab servers and mount remotely a directory from the NAS.

For some reason, Docker in DSN is called Container Manager, so that’s the first thing to install and setup; in my case the only setup needed was to create a dedicated directory in the NAS and assign it to Container Manager during the installation.

Let’s say this directory is /volume1/docker.

Now we need to create a few directories for Syncthing:

  • /volume1/docker/syncthing
  • /volume1/docker/syncthing/data
  • /volume1/docker/syncthing/tailscale
  • /volume1/docker/syncthing/tailscale-serve

Next we need to create a serve.json file with the following contents and upload it to /volume1/docker/syncthing/tailscale-serve:

{
  "TCP": {
    "443": {
      "HTTPS": true
    }
  },
  "Web": {
    "syncthing.my-tailnet.ts.net:443": {
      "Handlers": {
        "/": {
          "Proxy": "http://127.0.0.1:8384"
        }
      }
    }
  }
}

Remember to change syncthing.my-tailnet.ts.net with the name you decided to assign to your tailnet node and your tailnet domain.

Next we need to create the compose file for Syncthing, that will later be used to setup the containers in Container Manager:

---
services:
  syncthing:
    image: "syncthing/syncthing:1.29"
    container_name: "syncthing"
    restart: "unless-stopped"
    volumes:
      - "/volume1/docker/syncthing/data:/var/syncthing"
    # ports:
    #   - "8384:8384"
    #   - "22000:22000/tcp"
    #   - "22000:22000/udp"
    #   - "21027:21027/udp"
    environment:
      - TZ=UTC
      - PUID=1030
      - PGID=100
    depends_on:
      - tailscale
    network_mode: service:tailscale

  tailscale:
    image: "tailscale/tailscale:latest"
    container_name: "syncthing-tailscale"
    hostname: "syncthing"
    environment:
      - TS_AUTHKEY=tskey-auth-foo-bar-baz
      # - TS_EXTRA_ARGS=--advertise-tags=tag:syncthing
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_USERSPACE=false
      - TS_SERVE_CONFIG=/serve-config/serve.json
    volumes:
      - "/volume1/docker/syncthing/tailscale:/var/lib/tailscale"
      - "/volume1/docker/syncthing/tailscale-serve:/serve-config"
    devices:
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - net_admin
      - sys_module
    restart: unless-stopped

Few things need to be changed here; in syncthing’s service definition:

  • the volume’s path to match the directory you created in the previous step
  • the TZ environment variable if you want to use local time
  • PUID and PGID should match your user’s uid and gid in the NAS; log in via SSH and run id to find yours

In tailscale’s service definition:

  • the hostname, which will determine the name of this node in your tailnet
  • the TS_AUTHKEY environment variable with a valid auth key for your tailnet
  • the TS_EXTRA_ARGS environment variable in case you want to pass Tailscale extra parameters, for example to advertise a specific tag
  • the two volumes’ paths to match the directories created in the previous step

Also note that I specified a tag for synchting’s docker image (syncthing/syncthing:1.29), but you may want to use latest instead. I prefer to upgrade software in a controlled manner, when I have time to do it properly.

The ports section is intentionally commented out: we don’t need to expose any port here, but it’s good to have a reference of the ports used by Syncthing.

Now you can create a new project in Container Manager, paste the compose file we just created, build and start the containers. Syncthing should be available on your tailnet as https://syncthing.my-tailnet.ts.net (once again replace with your node’s name and tailnet’s domain). It might take a few seconds to load the first time, due to the automatic provisioning of the TLS certificate via Let’s Encrypt.

Your Syncthing installation should now be fully functional and reachable by other clients via Tailscale.

Now, I’m still figuring out this part, but:

  • Syncthing by default tries to open ports via UPnP on your firewall.
  • Syncthing enable Global Discovery by default, registering your node to a public database.
  • Syncthing, I think, will try to determine your public address (i.e. the address used by another nodes to reach it), to use as “Sync Protocol Listen Addresses”.

As this installation is meant to only exist within my VPN I’m changing a few settings in the “Connections” tab:

  • Sync Protocol Listen Addresses: tcp4://100.x.y.z:22000, quic://100.x.y.z.:22000 (use your node’s tailnet IP)
  • Enable NAT traversal: disabled
  • Global Discovery: disabled

I’m not entirely sure about the “Sync Protocol Listen Addresses”, because if Syncthing starts before Tailscale is fully connected it might fail to bind to those IP; restart: "unless-stopped" should take care of restarting it until Tailscale is fully connected, but I haven’t tested this yet.

Since we turned off Global Discovery, when we try to add a new client to Syncthing we’ll need to specify the address by hand, using something like tcp4://100.x.y.z:22000 or quic://100.x.y.z.:22000; I don’t know what works best and if Syncthing tries to use both even if you only specify one, so maybe start with the quic:// address first.