- Commands lose their .sh extension (create_personal_dir, gopass_bernina_setup, gopass_bernina_service_setup, kerberos_ticket_renew, migrate_to_network_drive, pixi_local_sync, pixi_rebuild_local, sync_personal_config); .sh now only marks files that are sourced. kerberos_ticket_renew.sh and pixi_local_sync.sh stay as links, since they are started at login. - Sourced files gain .sh (bpy-env, anaconda_env, anaconda_bernina_env, mm_bernina); the old names stay as links. - All references in scripts, the .desktop file and the docs updated; cheatsheet.md regenerated via list_bin_commands. - New pixi_pgroup_install: installs the shared bernina pixi env into a pgroup work folder (group-shared, with its own package cache) and makes it visible in JupyterHub as bernina-<exp>-<env>. - Fix '#/bin/bash' first lines (missing '!') in 11 scripts. - Stop tracking clutter (.nfs placeholders, __pycache__, vim swap file, .bak/_bak/~ copies, delme_*, empty 'me'); add .gitignore.
8.4 KiB
Syncing dotfiles/config across hosts with PSI's Partially Shared Home
How ~/network_drives, linux_home_links.yaml, and two helper scripts
(sync_personal_config, migrate_to_network_drive) keep personal
config (.vimrc, .condarc, SSH keys, ...) the same across every PSI
Linux host you log into. This is PSI's official
Partially Shared Home
feature.
See also: the gopass + gitea guide for why the gopass secrets store deliberately does not use this mechanism (§3 below).
The pieces
~/network_drives/home/ same network location on EVERY host
├── linux_config/ (SMB share, auto-mounted at login)
│ ├── ssh/, vimrc, condarc, ...
└── linux_home_links.yaml <- manifest: what goes where
~/ (LOCAL home, different per host, NOT shared)
├── .vimrc -> network_drives/home/linux_config/vimrc (symlink)
├── .bashrc (real local file, refreshed by copy)
└── ...
create-automatic-links-in-home.service (systemd --user) reads the
manifest and makes the local side match it, at login/ssh/`become`.
~/network_drives: your personal PSI network drive (SMB), same path on every Linux client — nothing to sync for what lives here.- Local
$HOME: per-host. Nothing follows you between machines unless something explicitly puts it there. linux_home_links.yaml: the manifest bridging the two.- The service (
/opt/pli/bin/create-automatic-links-in-home) applies it. Force it manually:systemctl --user restart create-automatic-links-in-home.service. Caveat: it runs via PAM session setup, which happens aftersshdhas already decided on pubkey auth — so it's structurally too late for anythingsshditself needs during authentication, notablyauthorized_keys(see the.sshgotcha below).
link: / copy: / download:
link:
'.vimrc':
to: 'network_drives/home/linux_config/vimrc'
copy:
'.bashrc':
from: 'network_drives/home/linux_config/bashrc'
download:
'.config/tool.config':
from: 'https://token@gitea.psi.ch/you/linux_config/tool.config'
link:— local path becomes a symlink intonetwork_drives. Edits go straight to the shared copy.copy:— local path is overwritten fromnetwork_drivesevery time the service runs (one-directional). Edit the network-drive source, not the local file, or your edit gets clobbered on next apply. Used for anything needed before the mount is ready (.bashrcat shell startup,authorized_keysat SSH auth time).download:— likecopy:, from an HTTP(S) URL instead.
Nesting-depth bug
The tool creates each symlink using the literal to: string, with no
adjustment for how deep the local path is — a relative target resolves
relative to the symlink's own directory, not $HOME. So it only works
out of the box for paths directly under $HOME (one segment). A nested
path (e.g. .local/share/gnome-shell/extensions, 3 segments) needs that
many ../ prepended to to::
'.local/share/gnome-shell/extensions':
to: '../../../network_drives/home/gnome-ext'
migrate_to_network_drive computes this automatically — prefer it
over hand-editing the yaml for anything not directly under $HOME.
No git repos on network_drives (SMB) — use /sf/bernina (NFS) instead
network_drives is an SMB/gvfs mount that doesn't support the atomic
rename/mkstemp writes git needs, or plain permission-preservation:
rsync: [receiver] mkstemp "...git/objects/.../abc123.XXXXXX" failed: Operation not supported (95)
Plain files are fine there (that's what link:/copy: are for) — just
never git init/clone/config (or gpg --gen-key) directly against a
path under network_drives; those all fail the same way, trying to
chmod an internal lock file. /sf/bernina (NFS4) doesn't have this
problem — git init/add/commit work cleanly on it. Rule of thumb:
check the mount type (df -T <path>) — nfs/nfs4 is generally fine,
cifs/smb/gvfs is not.
That said, an already-initialized repo (or GPG keyring), copied on
afterward with rsync -rlt (no -p/-a), keeps working normally from
network_drives — add/commit/push/pull and gpg encrypt/decrypt
all confirmed. gopass.md ("Store location") uses exactly this
init-locally-then-copy workaround as the preferred location for a
human's gopass setup, precisely because network_drives is already the
same mount on every host. Do this by hand only if you understand the
gotcha above; otherwise use the gopass scripts, which handle it for you.
The scripts
~/sync_personal_config (pre-existing) — seeds two hardcoded
items (.ipython, .condarc). Never edits an existing
linux_home_links.yaml, only prints the snippet to add by hand.
/sf/bernina/bin/migrate_to_network_drive (general-purpose) —
handles any file/directory, link: or copy::
migrate_to_network_drive <path-relative-to-HOME> [link|copy] [dest-name] [from-host]
migrate_to_network_drive .npmrc
migrate_to_network_drive .config/htop copy htop-config
migrate_to_network_drive .npmrc link "" saresb-cons-07 # pull from another host first
It refuses anything containing a .git directory, diffs before
overwriting anything already on the network drive, computes the correct
../ depth, and inserts the yaml entry itself (with a backup and a
confirmation prompt) rather than just printing a snippet. [from-host]
lets it scp the source from another host first — explicitly, or
offered automatically when the local path doesn't exist yet (defaults to
saresb-vcons-01).
For a file that belongs inside an existing shared subdirectory (e.g.
another key under linux_config/ssh/), pass the real relative path as
dest-name explicitly — the script's automatic flattening (.ssh/foo →
ssh-foo) won't match a path that should nest under ssh/:
migrate_to_network_drive .ssh/some_new_key link ssh/some_new_key
The .ssh gotcha: a whole-directory link: breaks SSH key login
Symptom: your key is genuinely in the shared authorized_keys, but a
fresh ssh to some host still falls back to a password — inconsistently.
Cause: if .ssh is linked as a single whole-directory entry, it
resolves through the same GVFS/SMB mount as network_drives generally —
which only exists while a graphical session for your uid is already
active on that host. sshd reads authorized_keys during authentication
itself, before the linking service has run (see the caveat above), so
a fresh connection only succeeds if some earlier session happened to
leave the mount warm.
Fix: split .ssh into per-file entries, with authorized_keys
specifically as copy: (a real local file, never a symlink):
link:
'.ssh/config':
to: '../network_drives/home/linux_config/ssh/config'
'.ssh/known_hosts':
to: '../network_drives/home/linux_config/ssh/known_hosts'
'.ssh/id_ed25519':
to: '../network_drives/home/linux_config/ssh/id_ed25519'
'.ssh/id_ed25519.pub':
to: '../network_drives/home/linux_config/ssh/id_ed25519.pub'
copy:
'.ssh/authorized_keys':
from: 'network_drives/home/linux_config/ssh/authorized_keys'
Each link: entry needs exactly one ../ (one segment below $HOME now
that the symlink is ~/.ssh/<file>, not ~/.ssh itself). Verified fix on
a real host: pubkey auth failed outright before the split, succeeded
immediately after, and survived a service restart with authorized_keys
re-copied identically. Apply the same split on any other host that hits
this, or use migrate_to_network_drive per file (remember the
explicit dest-name, e.g. ssh/config).
Troubleshooting
- A
link:entry resolves to "No such file or directory": the nesting-depth bug — check the../count against the path's depth. - Changes to a
copy:file disappear: you edited the local copy instead of~/network_drives/home/linux_config/<name>. - Nothing updates after editing the yaml: force
systemctl --user restart create-automatic-links-in-home.service. ~/network_drives/homemissing: runsetup-network-drives.- Trying to sync a git repo this way: don't — see above.
- SSH key login silently falls back to a password on some hosts: the
.sshgotcha above.