Lists every apt package, cargo tool, and install-script binary with its upstream, grouped by how it arrives — the three build deps are separated out since they exist only to compile the cargo tools and nothing uses them at runtime, and rustup is placed with the tools that force it rather than listed as a peer. Records the reasons that are invisible from the package list: bat's binary is batcat on Ubuntu so the cat alias depends on it, jq backs the Claude statusline, and rustup uses --no-modify-path because conf.d/rustup.fish already sources cargo's env. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 KiB
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.
What gets installed
Everything setup.sh puts on the box, and where it comes from.
apt packages
| Package | What for | Upstream |
|---|---|---|
fish |
the shell itself; setup.sh chshes to it |
fish-shell/fish-shell |
bat |
binary is batcat on Ubuntu; config.fish aliases cat to it |
sharkdp/bat |
ripgrep |
rg |
BurntSushi/ripgrep |
jq |
required by statusline-command.sh |
jqlang/jq |
git |
chezmoi clones the source repo with it | git/git |
curl |
fetches the rustup / starship / chezmoi installers | curl/curl |
nano |
$EDITOR; the nanox alias forces TERM=xterm-256color under Ghostty |
nano-editor.org |
build-essential |
cc/linker — cargo builds need it | GNU GCC |
pkg-config |
build dep for the cargo tools | pkg-config |
libssl-dev |
OpenSSL headers — atuin links against them | openssl/openssl |
The last three exist only to compile the cargo tools below. Nothing uses them at runtime.
Built from source with cargo install --locked
| Tool | What for | Upstream |
|---|---|---|
atuin |
shell history w/ sync + encryption | atuinsh/atuin |
eza |
ls replacement; backs the l / ll / lt / la aliases |
eza-community/eza |
macchina |
the banner config.fish prints on interactive start |
Macchina-CLI/macchina |
oxker |
terminal Docker container UI | mrjackwills/oxker |
This is the slow part — 10–20 minutes cold. Built from source deliberately, to match the versions running on the origin box rather than whatever apt ships. That makes the rust toolchain a mandatory build dependency:
| Tool | What for | Upstream |
|---|---|---|
rustup + cargo |
toolchain for the four tools above | rust-lang/rustup |
Installed via sh.rustup.rs with --no-modify-path — conf.d/rustup.fish
already sources ~/.cargo/env.fish, and the installer would otherwise add a
second PATH edit. Lands in ~/.cargo, owned by the user, not root.
Installed via upstream install scripts
| Tool | Where it lands | Upstream |
|---|---|---|
starship |
/usr/local/bin (root) |
starship/starship |
chezmoi |
~/.local/bin (user) |
twpayne/chezmoi |
Both are curl … | sh from the projects' own domains (starship.rs,
get.chezmoi.io).
Not installed here
fnm (node) and dtop/opencode are out of scope — their own installers write
conf.d/fnm.fish and conf.d/dtop.env.fish, which are .chezmoiignored.
config.fish adds ~/.opencode/bin to PATH only if that directory already
exists.
chezmoi on another VPS
setup.sh already does all of this. Use this path only when you want the dotfiles
without the provisioning layer (no apt, no rust, no chsh) — e.g. a box where the
tools are already there, or where you don't have root.
# 1. install chezmoi (user-local, no root)
sh -c "$(curl -fsLS get.chezmoi.io)" -- -b "$HOME/.local/bin"
export PATH="$HOME/.local/bin:$PATH"
# 2. pin the umask BEFORE the first apply — see Notes, this is load-bearing
mkdir -p ~/.config/chezmoi
printf 'umask = 0o022\n' >> ~/.config/chezmoi/chezmoi.toml
# 3. pull the source state (private repo — needs Gitea credentials)
chezmoi init https://git.domverse-berlin.eu/domverse/dotfiles.git
# 4. look before you leap
chezmoi diff
# 5. write it out
chezmoi apply
Notes on the above:
- Step 2 must come before step 4. The umask is read at apply time; applying first and fixing the config after leaves wrong modes on disk.
- Step 3 needs repo access. The repo is private. Either have
credential.helper=storealready populated, or clone over SSH:chezmoi init git@git.domverse-berlin.eu:domverse/dotfiles.git. - The source clone lands in
~/.local/share/chezmoi(chezmoi's default). This box is the exception — it setssourceDir = /home/crabix/projects/dotfilesin~/.config/chezmoi/chezmoi.tomlbecause the repo predates chezmoi. On a fresh box, don't setsourceDir; the default is whatsetup.shexpects. .chezmoirootcontainshome, so the source root ishome/, not the repo root.setup.shandREADME.mdsit outside chezmoi's view by design.- If
~already has real dotfiles,chezmoi diffin step 4 shows exactly what gets clobbered. Back them up with modes intact first:cp -a ~/.config/fish ~/.config/atuin ~/backup/dotfiles-preapply-$(date -u +%Y%m%dT%H%M%SZ)/ - Then do the Manual steps below (atuin key, git identity) — they are not
specific to
setup.sh.
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.
chezmoi gotchas
Learned the hard way while building this. Don't rediscover them.
-
chezmoi does not preserve file modes. It tracks only the
executable_andprivate_source attributes and derives the actual mode from the umask at apply time. Withumask 002(the default for this user),~/.config/atuin/silently goes0700→0775andconfig.toml0600→0664. Correct modes need both halves:- the source attribute —
private_atuin/private_config.toml, and umask = 0o022in~/.config/chezmoi/chezmoi.toml(setup.shwrites this).
Always
chezmoi diffbefore trusting a dotfiles repo on a new box; the mode lines are quiet and easy to scroll past. - the source attribute —
-
Never pipe
chezmoi applyinto a truncating command.chezmoi apply --verbose | head -20SIGPIPEs chezmoi partway through, leaving the apply incomplete — files sorting after the cutoff are never written. Worse,$?reports head's exit status (0), so it looks like it succeeded. Redirect to a file and read that instead:chezmoi apply --verbose > /tmp/apply.log 2>&1; echo "exit=$?" -
The file in
~is a copy, not a symlink. Editing~/.config/fish/config.fishdirectly gets silently clobbered on the next apply. Usechezmoi edit. Thefishconfigalias points atchezmoi editfor exactly this reason. -
chezmoi initon a box with existing dotfiles overwrites without asking once youapply.chezmoi difffirst, always.
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.