I.M Blogging

Simplifying Syncthing With Nix

Syncthing is a wonderful tool that I use to sync folders quickly and securely across my devices without having to use a proprietary tool like Dropbox. You can use this tool on almost any system, and I definitely would recommend it to anyone who wants to sync files across their devices due to its ease of use and security.

This post doesn't go over how you would use Syncthing normally as you would on any other distro, instead showing off how nix can be used to manage Syncthing. Which can be especially great when you need to manage a lot of devices or folders. If you want a standard guide I would recommend Syncthing's great guide and documentation.

The Issue

Something that may come up as a hurdle with Syncthing is when dealing with networks of more then just a few devices/folders (i.e more then 3). As adding new devices or managing shared folders, becomes far more tedious as each individual machine will need to be configured to manage the network as a whole1.

For example, if you had five devices that you wanted to link together. After installing and setting up Syncthing on each machine, device one will need to connect device two. Then device two will need to accept the connection from device one, this will be repeated between each device pair if you want all devices to connect to each other. After each device is connected, sharing a folder will require a similar process of creating, configuring, and sharing with all relevant devices. Then accepting the folders, and configuring the there settings on the new devices as well.

This can be very annoying when dealing with large number of devices or folders that need to be connected together. While there are methods within Syncthing itself, such as autoAcceptFolders. I feel that the nix method I will show makes this all so much easier.

With Nix

When you add in nix to this you get the ability to define all devices, and the folders that they share in one file. With changes reflected across all devices without the need to individually manage each devices' folders and settings.

This is done using the Syncthing service in nix, which has the options services.syncthing.settings.devices which lets you define all devices in the network and services.syncthing.settings.folders which defines folders. For example if you had three devices you could define them all in the service like so.

  services.syncthing.settings.devices = {
            "device1" = {id = "XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX";}; 
            "device2" = {id = "XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX";};
            "device3" = {id = "XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX";};
          };

With each device getting a name, and their device ID set. You can then use these devices when defining folders. For example.

  services.syncthing.settings.folders = {
            "/home/youruser/sync" = {
                  devices = ["device1" "device2" "device3"]
            };
          };

This is a pretty simple folder config, but you can already see how much easier it will be managing devices and folders with everything defined in one file. As any changes to which devices share which folders will be automatically made with and devices that use this service configuration. Adding devices is simply done by extending services.syncthing.settings.devices and then adding them to the respective folders.

  services.syncthing.settings.devices = {
            "device1" = {id = "XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX";}; 
            "device2" = {id = "XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX";};
            "device3" = {id = "XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX";};
            "device4" = {id = "XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX";};
          };
  services.syncthing.settings.folders = {
            "/home/youruser/sync" = {
              devices = ["device1" "device2" "device3" "device4"]
            };
          };

That's it. Just update the config the machines are using and then they will sort out the rest.

Adding non-nix hosts

Now this is great for managing Syncthing between NixOS hosts, but you may be wondering how to add non-NixOS host into your config. This is pretty simple, as adding the non-nix device ID to the devices list and then treating them the same as a NixOS host. Though you'll need to manually accept the connections and folders on the devices that aren't managed by nix.

Manual management

If you want to have manual management of device connections and folders , set overrideFolders, and overrideDevices to False, along with localAnnounceEnabled set to true. This will allow you to manage the Syncthing service outside of the configuration set in the file. I don't recommend this but it is an option that you can take if it's what your most comfortable with.

Also to note, without both override options set to false changes that you make through the Syncthing UI will be reset after the system restarts.

Other Config Options

While everything till this point will give you most of what you need to know, there are some extra options that you can configure to fine tune your personal setup.

services.syncthing.settings.options.urAccepted

Syncthing reports anonymous usage data back to the development team, normally this would show a pop-up when you first access the web UI. While in NixOS you can declare your decision in the service itself. A negative number is a no, and a positive number is a yes. While 0 (the default) will prompt the software to ask you through the web UI as normal.

services.syncthing.settings.options.relaysEnabled

The relay system in Syncthing is how devices on separate networks that normally wouldn't be able to connect with each other, interact. It's useful when you need your devices to connect even when they move to another location/network (i.e your laptop, or phone), or if some devices are just on disparate networks normally. Relaying is disabled by default, though setting services.syncthing.settings.options.relaysEnabled to true will allow the device to connect through the relay service provided by the Syncthing team.

It's possible to reconfigure the relay service that your network will make use of, helpful if you self-host a relay or use one from another provider2. Though since I make use of the default relay service I'm not 100% certain on how to set this up3.

services.syncthing.settings.folders.<name>.versioning

Versioning is important as it offers some protection against deleting files by accident4. To enable versioning on a folder set services.syncthing.settings.folders.<name>.versioning.type to a supported versioning strategy5. Then pass the arguments through services.syncthing.settings.folders.<name>.versioning.params to override any default values you want5. This is the example setup of a staggered folder in the nix docs.

  services.syncthing.settings.folders.<name>.versioning = {
        type = "staggered";
        params = {
          fsPath = "/syncthing/backup";
          cleanInterval = "3600";
          maxAge = "31536000";
        };
      };

Basic Template

Putting it all together you might have something like this for the service configuration.

  # SPDX-FileCopyrightText: 2024 Imran Mustafa <imran@imranmustafa.net>
  #
  # SPDX-License-Identifier: GPL-3.0-or-later
  {
    pkgs,
    lib,
    config,
    ...
  }: {
    options = {
      syncthing.enable = lib.mkEnableOption "Syncthing";
    };
    config = lib.mkIf config.syncthing.enable {
      services.syncthing = {
        enable = true;
        dataDir = "/home/YOUR_USER";
        openDefaultPorts = true;
        settings = {
          options = {
            urAccepted = -1;
            relaysEnabled = True;
          };
          devices = {
            "device1" = {id = "XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX";};
            "device2" = {id = "XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX";};
          };
         folders = {
            "/home/YOUR_USER/sync" = {
              devices = [device1 device2];
              versioning = {
                type = "staggered";
                params = {
                  fsPath = "/syncthing/backup";
                  cleanInterval = "3600";
                  maxAge = "31536000";
                };
              };
            };
         };
      };
    };
  }

Footnotes


1

I'm assuming the network is maximally connected to show off the problem.

2

A list of public relays is available at https://relays.syncthing.net/, These are not provided by the Syncthing team.

3

Sorry if you needed that information.

4

Though this wouldn't count as a proper backup.


Tags: