The impermanence bind-mount symlink trap
Moving a directory into the persistence list on an impermanent NixOS host looks trivial
until nixos-rebuild switch dies with:
home-lowcache-.gemini.mount: Mount path /home/lowcache/.gemini is not canonical (contains a symlink).
Here is why it happens, and the one rule that avoids it.
The setup
On this host, root is a tmpfs that is wiped every boot; anything that must survive lives on
/persist and is surfaced back into $HOME by impermanence. The mechanism is a systemd
bind mount per persisted directory: the real data lives at /persist/home/lowcache/<dir>
(the mount source), and systemd bind-mounts it onto ~/<dir> (the mount target) at
activation.
Adding a directory is a one-line change to the persistence list:
home.persistence."/persist".directories = [
# ...
".gemini"
];
The trap
I was migrating ~/.gemini from an old repo symlink to a real persisted directory. To avoid a
dangling link in the window between moving the data and rebuilding, I “helpfully” pre-created a
bridge symlink:
ln -sfn /persist/home/lowcache/.gemini ~/.gemini # <- the mistake
That is exactly what breaks the mount. systemd refuses to bind-mount onto a path whose target
contains a symlink. The target must be canonical. Every other persisted directory works
because its ~/<dir> is a plain, empty directory that the bind mount covers. The symlink made
.gemini the odd one out.
The failure is also self-obscuring: a partial activation keeps re-creating the (empty) mount source, so it never looks resolved, and the data you moved sits stranded in whatever you renamed it to.
The rule
For bind-mount persistence, the target is always a real, empty directory, never a symlink. Let the data live at the source and let systemd do the mounting:
# target: a canonical empty mountpoint (NOT a symlink)
rm -f ~/.gemini
mkdir -p ~/.gemini
# source: the actual data, on /persist
mv /persist/home/lowcache/.geminibak /persist/home/lowcache/.gemini
Then nixos-rebuild switch mounts source onto target cleanly. Verify with ls ~/.gemini (data
shows through the mount) and findmnt ~/.gemini.
The general lesson: symlink bridges are a reflex from mkOutOfStoreSymlink-style persistence,
where ~/<dir> is a symlink by design. Under bind-mount persistence the model is inverted: the
target must stay canonical. Know which of the two your host uses before you reach for ln -s.
Written from the machine it describes. More field notes: the archive · RSS.
Working on something in this territory? I take on scoped engagements.