Bootstrapping dotfiles with chezmoi in a DevContainer
chezmoi manages dotfiles as a templated, version-controlled source that renders per machine — ideal for DevContainers, where the same dotfiles must adapt to an ephemeral container user without leaking secrets into the image. This page bootstraps chezmoi during container creation so your shell, editor, and Git config land correctly on first attach. It extends the Automating Dotfiles Sync Across Containers cluster.
Prerequisites
- A dotfiles repository managed by chezmoi (
chezmoi initalready run locally) - Network access to clone the dotfiles repo at create time (or a vendored copy)
- A non-root
remoteUser
How-To Steps
-
Install and apply chezmoi in
postCreateCommand, pointing it at your dotfiles repo:{ "image": "mcr.microsoft.com/devcontainers/base:bookworm@sha256:2c5a...", "postCreateCommand": "sh -c \"$(curl -fsLS get.chezmoi.io)\" -- init --apply yourgithubuser", "remoteUser": "vscode" }Verify:
chezmoi managedlists your tracked files and~/.zshrcexists. -
Keep secrets out of the image by sourcing them from the host environment at render time. In a chezmoi template (
dot_netrc.tmpl):machine api.internal login {{ env "API_USER" }} password {{ env "API_TOKEN" }}Pass the host values through without baking them in:
{ "remoteEnv": { "API_USER": "${localEnv:API_USER}", "API_TOKEN": "${localEnv:API_TOKEN}" } } -
Make re-apply idempotent so a rebuild or restart re-renders cleanly:
{ "postStartCommand": "chezmoi apply --force" } -
Confirm a clean render with no diff after apply:
chezmoi apply && chezmoi diff # diff should be empty
Common Pitfalls
| Symptom | Root Cause | Remediation |
|---|---|---|
| Secrets end up in the image | Tokens hardcoded in dotfiles | Use chezmoi templates reading remoteEnv values |
| Dotfiles overwrite each rebuild but lose edits | Editing rendered files, not the source | Edit via chezmoi edit; never touch rendered targets |
| Apply fails on missing template var | Host env var not forwarded | Add it to remoteEnv via ${localEnv:...} |
| Wrong home directory | Applied as root before remoteUser switch | Run apply in postCreateCommand as remoteUser |
Conclusion
The invariant: the chezmoi source is the truth, rendered per container with secrets injected from the host at apply time — never committed. Bootstrap with init --apply in postCreateCommand and re-apply idempotently on start, and your personal environment is reproducible without leaking credentials into the image.
FAQ
Why chezmoi over a plain symlink script? Templating. chezmoi renders the same source differently per machine and pulls secrets from the environment, which a static symlink approach can’t do safely in a shared image.
Where should the dotfiles repo live for offline use?
Vendor a copy into the workspace and run chezmoi init --source ./dotfiles --apply so creation doesn’t require network access.
Related
- Automating Dotfiles Sync Across Containers — the parent cluster.
- How to sync dotfiles across multiple DevContainers — the symlink-based approach.
- Persisting Zsh history across container rebuilds — keeping shell state alongside dotfiles.