Skip to content
Go back

Syncthing for Dotfiles: Sync vs Manage

By SumGuy 11 min read
Syncthing for Dotfiles: Sync vs Manage
Contents

The Reader Question That Started This

A while back I wrote about running Syncthing over untrusted VPS relays, and someone in the comments asked the entirely fair question: “Why not just use chezmoi for this?” Great question. Different tool, different job — but it’s worth actually answering instead of hand-waving it away, because the honest answer is “it depends on what problem you think you’re solving,” and most people don’t realize there are two different problems hiding under the word “dotfiles.”

So let’s settle it properly. Should you use Syncthing to sync your dotfiles? Sometimes yes. Often no. Almost always in combination with something else. Let’s get into why.

What Syncthing Actually Does (and Doesn’t)

Here’s the thing people miss: Syncthing has no idea what a dotfile is. It doesn’t know .bashrc is more important than a screenshot from three years ago. It’s a P2P, block-level, continuous file synchronization engine — full stop. You point it at a folder on Machine A, pair it with Machine B, and from that point on, whatever bytes exist in that folder on one device get replicated to the other, in near-real-time, over an encrypted transport. No commits. No apply step. No “did I remember to push?” ritual. You save the file, and a few seconds later it exists elsewhere too.

That’s genuinely magical for a lot of use cases. It is not, on its own, a dotfile management strategy — it’s a mirror. And mirrors reflect everything, including the ugly stuff.

Compare that to chezmoi, which treats your home directory as a build target. Your actual dotfiles live in a source-of-truth directory (usually ~/.local/share/chezmoi, backed by a git repo), and what ends up in ~/.bashrc is the rendered output of running templates against that source, with an explicit step to make it happen:

Terminal window
chezmoi init --apply [email protected]:yourname/dotfiles.git

One command. Pulls the repo, renders every .tmpl file for this specific machine, and writes the result into your home directory. That “render for this specific machine” part is the whole ballgame, and it’s where Syncthing structurally can’t compete — more on that in a second.

If you want the full manager-vs-manager breakdown — chezmoi vs GNU Stow vs a bare git repo — I already did that fight in a separate post. This one’s not about which manager wins. It’s about whether you should be using a manager at all, or just letting a sync daemon handle it.

The Case Against Syncthing as a Dotfile Tool

It propagates your mistakes just as fast as your good changes

This is the one that should scare you a little. Syncthing doesn’t have opinions about whether a change is good. You save a half-edited .vimrc with a dangling bracket, or you rm your .ssh/config by accident while cleaning up — Syncthing sees a changed file and does exactly what it’s designed to do: replicate it. Within seconds, your broken config (or your missing config) is now broken or missing on every other device sharing that folder too. There’s no “wait, are you sure?” There’s no review step. That’s not a bug, that’s the entire value proposition of a sync engine — continuous, unattended replication. It’s just a value proposition that cuts both ways.

Syncthing does have a safety net: file versioning. When you enable it on a shared folder, replaced or deleted files get moved into a hidden .stversions directory instead of being silently gone forever. You’ve got four strategies to choose from:

Turn on Staggered for your synced folder and a busted .vimrc becomes recoverable — you can dig the last-good copy out of .stversions by hand. But notice what that is: a recycle bin, not source control. There’s no commit message, no diff, no branch, no “why did I change this on March 3rd.” Compare that to a dotfile manager backed by git, where git log -p -- .vimrc tells you exactly what changed and when, and git revert undoes it cleanly across every machine the next time you pull. .stversions is “I panicked and need last week’s file back.” Git history is “I can reconstruct the reasoning.” Those are not the same safety net.

No such thing as “this file, but different per machine”

Here’s the structural problem, and it’s the biggest one: your work laptop and your personal desktop are not the same computer. Work wants a different git user.email. Your desktop has a different $PATH because you installed Homebrew in a different spot, or you’re not running the corporate VPN client wrapper in your shell profile. Your ultrawide monitor at home needs different DPI/scaling settings than the laptop’s built-in panel.

Syncthing has no concept of “sync this file, but adjust these three lines for this specific device.” A file is a file. Whatever bytes are on disk get mirrored, verbatim, to every other node in the share. Your options are: keep them byte-identical (and give up on legitimate per-host differences), maintain separate synced folders per host-type (which defeats half the point of syncing), or start excluding specific files from sync and manage those by hand anyway — at which point you’re just doing manual dotfile management with extra plumbing.

chezmoi solves this natively with templating. A file like ~/.gitconfig becomes a source template dot_gitconfig.tmpl:

dot_gitconfig.tmpl
[user]
name = KingPin
email = {{ if eq .chezmoi.hostname "work-laptop" }}[email protected]{{ else }}[email protected]{{ end }}
[core]
pager = {{ if eq .chezmoi.os "darwin" }}delta{{ else }}less{{ end }}

chezmoi apply renders that template against machine-specific data (.chezmoi.hostname, .chezmoi.os, or values you define yourself in .chezmoi.toml.tmpl) and writes out the correct ~/.gitconfig for that machine. One source file, N correct outputs. This is the single biggest reason Syncthing struggles as a pure dotfile tool — it’s not a missing feature you could bolt on, it’s a fundamentally different model (mirror vs. render).

Secrets ride along in the clear

chezmoi’s whole secrets story is about keeping sensitive values out of the source repo entirely. You template them in from an external secret store — age-encrypted files, 1Password, Bitwarden, HashiCorp Vault — and chezmoi pulls them in at apply-time:

private_dot_netrc.tmpl
machine api.example.com
login {{ .netrc_user }}
password {{ (bitwarden "item" "api.example.com").login.password }}

The repo itself never contains the plaintext password. That matters a lot if your dotfiles repo lives on GitHub, or you ever apply it on a machine you don’t fully trust.

Syncthing has no redaction layer, because it doesn’t know what a “secret” is any more than it knows what a dotfile is. Whatever plaintext exists in the synced folder — your .netrc, your API tokens in a shell profile, an SSH key you foolishly keep in ~/dotfiles/.ssh/id_ed25519 — gets synced as-is, in full, to every paired device. In fairness, this isn’t a security hole in the traditional sense: Syncthing’s transport is end-to-end encrypted and P2P, no third-party server ever sees the plaintext. But “the pipe is secure” and “the secret is redacted from the source of truth” are different guarantees. With Syncthing, the secret exists in full, unredacted form, on every single node you’ve paired. Lose control of any one of them and it’s gone.

Bootstrapping a fresh machine takes more steps, not fewer

With a dotfile manager, day one on a new machine is one command (chezmoi init --apply <repo>) and you’re done — install the binary, run the command, walk away with a fully configured environment. Syncthing’s bootstrap is: install the Syncthing binary/package, start the daemon, open the web GUI, generate this device’s ID, go to the other device and manually add the new device by pasting its ID, accept the pairing request on both ends, then share the specific folder from the existing device to the new one, and wait for the initial full sync to finish before your .bashrc actually exists. It works, but it’s more clicks, both machines need to be online at the same time at some point, and there’s no equivalent of “apply a template for this specific host” baked into the flow — because again, Syncthing doesn’t do templates.

Where Syncthing Actually Wins

None of the above means Syncthing is bad — it means it’s solving a different problem than “manage my dotfiles well.” Here’s where it’s genuinely the better tool:

It’s continuous and frictionless. No commit, no push, no apply. You edit a file, it’s on the other machine a few seconds later. No “oh no, I forgot to sync before I left the house” moment ever again.

It syncs stuff dotfile managers have no business touching. Your Neovim plugin directory (~/.local/share/nvim), shell history, ~/.ssh/known_hosts, whole tool-state directories, even browser profiles or large binary blobs. Nobody wants to chezmoi add a gigabyte of plugin cache into a git repo. Syncthing doesn’t care that it’s not “config” in the traditional sense — it just replicates the directory.

No third party required, at all, if you don’t want one. No GitHub, no git server, nothing. Pure P2P between devices you own. If your threat model is “I don’t want my dotfiles anywhere but my own hardware,” Syncthing gets you there without a hosting decision.

It’s fantastic for the identical-environment case. Two personal Linux boxes you want to behave like clones of each other — same shell history, same tool state, same everything, no per-host differences to reconcile? That’s exactly the scenario Syncthing was built for. The “no per-host config” weakness above stops being a weakness the moment there genuinely isn’t supposed to be a per-host difference.

The Setup That Actually Makes Sense: Both

Here’s the honest verdict, and it’s not “pick a side.” Syncthing is the wrong tool if your actual problem is managing dotfiles well across machines that need to differ from each other — for that, use chezmoi (or Stow, or a bare repo, pick your fighter). Syncthing is the right tool when your actual problem is keeping your whole environment and tool-state in lockstep across machines you fully control, including stuff that has zero business being tracked in git.

The mature setup uses both, and they don’t step on each other because they’re not touching the same files:

.stignore
// Managed by chezmoi — don't let Syncthing anywhere near these.
// chezmoi apply is the only thing allowed to write them.
.bashrc
.zshrc
.gitconfig
.vimrc
.config/starship.toml

Anything you don’t list in .stignore syncs by default — that’s the point. So with just those five lines excluded, Syncthing quietly owns everything else in the folder: the big, boring, host-agnostic tool-state chezmoi has no opinion about, like ~/.local/share/nvim, ~/.local/share/zsh, ~/.ssh/known_hosts, or ~/.cache/gh. (.stignore is a deny-list — an exclamation-mark ! prefix only matters as an exception carved out of a broader ignore rule above it, so there’s no need for one here.) chezmoi owns the templated, per-host, secret-bearing config — the stuff that genuinely needs to differ between your work laptop and your home desktop, or that shouldn’t leak a plaintext token if the repo ever gets cloned somewhere it shouldn’t. Syncthing owns the big, boring, host-agnostic state that just needs to be the same everywhere and that no dotfile manager would sanely track anyway.

Neither tool is wrong. Using Syncthing as a full replacement for a dotfile manager is the mistake — not because Syncthing is bad software, but because it’s solving “keep bytes identical,” and dotfiles across heterogeneous machines are fundamentally a “render the right bytes for this specific machine” problem. Use the right tool for each half, and you stop fighting both of them.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
Postgres HA: Patroni + etcd + HAProxy
Next Post
Btrfs RAID 5/6: Still Don't

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts