Migration
Migrating from yadm
yadm is the closest neighbour: both track real files in $HOME with git, both template per host, both encrypt secrets. The migration is mostly a mechanical re-home of files plus a translation of yadm's alternate-file suffixes into chezmoi templates.
Concept mapping
| yadm | Here | Note |
|---|---|---|
yadm add / yadm commit | dot add then a normal git commit in the source dir | Source tree is a plain git repo |
$HOME is the work tree | Files live in defaults/ and are rendered into $HOME | The big conceptual change |
##os.Linux, ##hostname.foo suffixes | {{ if eq .chezmoi.os "linux" }} in a .tmpl | One file with branches, not N files |
yadm alt | chezmoi apply | Automatic; no separate step |
yadm encrypt (GPG) | dot secrets (age + sops) | Different crypto; see below |
yadm bootstrap | install/provision/run_onchange_* | Runs on apply, idempotent |
yadm status / diff | dot status / dot diff | Same intent |
1. Inventory and back up
Keep yadm-inventory.txt: it is your checklist, and step 5 diffs against it.
2. Install alongside yadm
The installer does not touch yadm's repo (~/.local/share/yadm/repo.git) or its config, so both can coexist while you migrate.
# Download and verify the release installer per docs/guides/INSTALL.md first.
If you want to bring your own dotfiles repo rather than the maintainer's defaults, use dot init instead:
3. Move files across
Plain (non-alternate) files first:
while ; do
case "" in
*##*) continue ;; # alternates handled in step 4
esac
done
dot add wraps chezmoi add, so a file in ~/.config/foo/bar lands at defaults/dot_config/foo/bar, and ~/.gitconfig at defaults/dot_gitconfig.
4. Translate alternates into templates
For each ##-suffixed file, replace the family with one .tmpl.
yadm:
~/.gitconfig##os.Darwin
~/.gitconfig##os.Linux
Here — defaults/dot_gitconfig.tmpl:
[user]
name = {{ .name }}
email = {{ .email }}
{{ if eq .chezmoi.os "darwin" }}
[credential]
helper = osxkeychain
{{ else if eq .chezmoi.os "linux" }}
[credential]
helper = cache --timeout=3600
{{ end }}
The common yadm conditions map as:
| yadm suffix | chezmoi expression |
|---|---|
##os.Darwin | eq .chezmoi.os "darwin" |
##os.Linux | eq .chezmoi.os "linux" |
##hostname.foo | eq .chezmoi.hostname "foo" |
##user.alice | eq .chezmoi.username "alice" |
##distro.Ubuntu | eq .chezmoi.osRelease.id "ubuntu" |
##default | the {{ else }} branch |
Verify before applying — templates fail loudly, but only when rendered:
5. Migrate encrypted files
yadm uses GPG; this framework uses age via sops. There is no automatic converter, and that is deliberate — re-encrypting secrets is a step you should perform consciously.
# Decrypt with yadm, in a directory that is not the repo.
&&
# Set up age and re-encrypt.
&&
Details and the provider model: ../security/SECRETS.md.
6. Apply and verify
Then diff against the inventory to catch anything missed:
while ; do
[ ||
done
7. Retire yadm
Only after a full working day on the new setup:
# and remove the yadm package via your package manager
Rolling back
At any point before step 7, yadm is untouched:
What you gain, what you lose
Gain: one templated file instead of alternate families; the dot CLI (doctor, health, heal, rollback, attest); multi-shell parity including fish, nushell and PowerShell; signed and attested releases; fleet commands for more than one machine.
Lose: $HOME is no longer a git work tree, so git status in your home directory stops being meaningful — you use dot status instead. GPG-encrypted files become age/sops. yadm is a single ~2k-line script; this is a larger surface. And yadm's bootstrap is one file, whereas provisioning here is spread across install/provision/.