Folks - I’m building a Helm chart to install Syncthing in Kubernetes. Because A) Syncthing listens on 22000, and B) out-of-the-box k8s NodePort services typically start in the >=32000 range, then it requires to configure Syncthing to communicate on >=32000 per: Syncthing Configuration — Syncthing v1.28.0 documentation.
This can be done in the UI but - automating it at install time is non-trivial since Syncthing appears to only accept one config XML in its entirety.
My question is - is there a way with Syncthing to supplement the default /var/syncthing/config/config.xml file with individual setting overrides? I don’t see a command line arg for this one setting under syncthing serve --help (which is perfectly reasonable otherwise there would have to be dozens of args which would be undesirable.)
If Syncthing only accepts the full XML then the Helm chart has to include an entire XML and the chart has to interpolate this one setting into it. This is fragile from a chart perspective for obvious reasons.
It’s more natural from a Kubernetes/Helm perspective to support the following:
Adopt the Linux configuration directory approach (i.e. conf.d) where all files in the directory are processed in name order allowing override files to exist with the default configs. Then the Helm chart could mount this additional config as a separate file into the same directory as the default config.
Or - support env vars for all these settings as overrides
Or - support a configuration override file (e.g. via command line arg like --additional-configs=/path/to/file)
Is anyone running Syncthing in k8s and if so - have you addressed this consideration and if so how? Thanks in advance for any help.
You can use the CLI to change it after creating the config.xml (when Syncthing is already running):
syncthing cli config options raw-listen-addresses 0 set ...
Or simply create a basic config.xml containing only the few settings you need to override, then let Syncthing respect that when first generating its certificate and stuff:
There’s been a recent similar discussion, for another field. My opinion from there still stands: I don’t think it’s viable to add special cases for single fields (either as command line args or special config fields), it should be generic. A few options to approach that were mentioned, I don’t have time to right now to search for them. The building blocks are already there though. Already now you could do it after startup either through syncthing cli config or equivalently the rest API. And if it needs to be before startup, you could e.g. add an operation to the cli to just populate the config, but not start. Then extend syncthing cli config to be able to modify config directly instead of through the API. There are probably other options, the point is the building blocks are there to do something generic.
I get it. Do you think if the startup merge functionality were refactored to be a true merge - that would be a viable solution? Right now there are a few “special cases” in the merge handling but conceptually the logic could be changed to:
Read partial config and convert to Go struct
Generate default (as go struct)
Merge partial into default accepting only populated (non-nil) values
Write result as XML
I have not research Go struct merging (other than two minutes just now) but its likely a common problem with a good solution somewhere…
What you describe to me sounds like what we do. I don’t think this is a problem of “implementing true merge”, at least I don’t see a true merge conceptually: Your example shows an empty device with just a name, while the identifying and defying property of a device is its ID. While we could add code such that a single empty device with a name means that is the local device, and treat it accordingly, that’s an implicit, magic (as in it’s happening because we wish for it, not following some rule), unexpected behaviour. What’s wrong with setting the name after generating the initial config?
I wouldn’t say there’s anything wrong with it. It’s just more idiomatic in the Helm/Kubernetes world to say (conceptually) here is configuration for this workload and be done.
What we presently have is here is some initial configuration for this workload and then wait for the workload to reach some state (config.xml fully generated) and then poke it with some additional configuration because the startup configuration doesn’t support all configurables.
Hey I’m not complaining - this is a great piece of software. As a k8s integrator I’m just wanting it to play nice with Helm and Kubernetes. This particular case is complex because the devce ID is derived - it appears - from PKI materials and is coupled with the “friendly” device name. So that makes it particularly hard for the Helm chart to intervene with. And with Kubernetes / Helm you want everything to be automatiable, declarative, and GitOps-enabled…
Yeah, Syncthing is not a stateless application. Consider running it like you would something like PostgreSQL. There you’ll find an entire ecosystem of operators and stuff to manage it once it’s running to create users and roles and databases and such, and frankly Syncthing is kind of similar. I could see an operator doing the things you’re envisioning, together with SyncedFolder and PeerDevice CRDs and so on, though this is obviously at an entirely different effort level than what you’re looking at right now.
For something like a helm chart I’d probably allow the user to have control by explicitly specifying the device ID certificates and config (stub or not), or let Syncthing manage it. It’s more difficult to do some sort of middle road.
Yep - I’ll take the approach for now to configure what is supported on startup then have a sidecar that waits for Syncthing to be ready and then makes any remaining tweaks using the REST API. Thanks for everyone’s help.
We have the var LocalDeviceID = repeatedDeviceID(0xff) as a defined special case value already. I could see us adding some code to parse a config section mentioning that special ID as applying to the local ID of the running instance, and treating the section as such. But it’s a very specific solution for a very small set of use cases.
Obvious problem if we added it: What to do when device sections are present for both the LOCAL-ID special value and the real device ID?