Two layers with a deliberate privilege split: setup.sh does the root work (apt, rustup, cargo tools, starship, chsh) and hands off to chezmoi, which owns everything under $HOME. Tools are built with `cargo install --locked` to match the versions currently running rather than whatever apt ships, at the cost of a 10-20 min cold setup. Excluded on purpose: the atuin sync key and ~/.git-credentials (secrets), ~/.claude/settings.json (per-box decision), fnm/opencode conf.d (out of scope), and fish_variables (machine-local). chezmoi tracks only the executable and private attributes and derives modes from the umask, so setup.sh pins umask 022 and atuin uses private_ to keep 0700/0600. Without both, a 002 umask silently relaxes them to 0775/0664. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dotfiles
Shell + tooling setup for a fresh Ubuntu box: fish · starship · atuin · eza · macchina, plus the Claude Code statusline.
Two layers, deliberately split:
| Layer | Owns | Tool |
|---|---|---|
| Provisioning | apt packages, rust toolchain, starship, default shell | setup.sh (root) |
| Dotfile state | everything under $HOME |
chezmoi (user) |
setup.sh does the root work and then hands off to chezmoi. Nothing that needs
sudo lives inside chezmoi, and nothing under $HOME is touched by the script.
Fresh box
git clone https://git.domverse-berlin.eu/domverse/dotfiles.git
cd dotfiles
sudo bash setup.sh
Run it as your normal user via sudo — the script reads SUDO_USER to know whose
home to set up, and refuses to run as root directly.
Expect 10–20 minutes: atuin, eza, macchina and oxker are built from source with
cargo install --locked, which needs the rust toolchain. That's the deliberate
trade for matching the currently-running versions exactly rather than taking
whatever apt ships.
Day-to-day
Configs are managed by chezmoi, so the file in ~/.config is a copy, not a
symlink. Edit through chezmoi or your change gets overwritten on the next apply:
chezmoi edit ~/.config/fish/config.fish # edit source
chezmoi diff # what drifted on this box?
chezmoi apply # write source -> $HOME
chezmoi cd # jump to the source repo, then git push
The fishconfig alias is wired to chezmoi edit for this reason.
What's tracked
home/
dot_bashrc ~/.bashrc (stock + atuin init)
dot_gitconfig ~/.gitconfig
dot_config/fish/config.fish.tmpl aliases, eza, init lines
dot_config/fish/conf.d/rustup.fish sources ~/.cargo/env.fish
dot_config/atuin/config.toml
dot_config/starship.toml stock-equivalent, pinned
dot_claude/executable_statusline-command.sh statusline (needs jq)
What's deliberately NOT tracked
See home/.chezmoiignore. The load-bearing exclusions:
~/.local/share/atuin/key— the sync encryption key. Tracked by hand insecrets.yml. Restore it before the firstatuin sync, or history encrypted on other hosts will not decrypt.~/.git-credentials— plaintext tokens (.gitconfigsetshelper = store).~/.claude/settings.json— pins the model and setsskipDangerousModePermissionPrompt; a per-box decision, set by hand.conf.d/fnm.fish,conf.d/dtop.env.fish— node/opencode toolchains, out of scope. Their installers write these themselves.fish_variables— machine-local state, fish rewrites it.
Manual steps after setup.sh
The script prints these; repeated here because they're easy to miss:
- atuin — restore key from
secrets.ymlinto~/.local/share/atuin/key, thenatuin login -u <user>andatuin sync. - git identity — not set globally, only
credential.helper=store:git config --global user.name '<name>' git config --global user.email '<email>' - Log out and back in for the fish shell change to take effect.
Notes
config.fishaliasescattobatcat— on Ubuntu the bat binary really isbatcat, so the aptbatpackage is a hard dependency of the fish config.- rustup is installed with
--no-modify-path:conf.d/rustup.fishalready sources~/.cargo/env.fish, and the installer would otherwise add a second PATH edit. starship.tomlis currently equivalent to stock defaults. It exists so the prompt is pinned to a known config rather than tracking version defaults.