FAQ
Do I need to fork this collection?
No. Create your own personal repo that references the collection in requirements.yml; your identity, repositories, and aliases live there, the shared roles stay here. See Getting started.
Why Ansible instead of a dotfiles manager like chezmoi?
Dotfiles Pro manages the whole machine (packages, system config, repositories, and dotfiles), not just the files in $HOME. If you only sync files, chezmoi is simpler; the full comparison is in Alternatives.
Why not just let my agent (Claude, etc.) configure my system?
An agent is great at one-off changes, but it improvises each time, on a live machine, with no record of what it touched. Dotfiles Pro is the opposite, the desired state is declared once in YAML and applied idempotently, so the same run reproduces the same machine and a fresh box converges to it.
You still get the agent: point it at your vars and roles, not at $HOME, and it edits the declaration that you review, version, and replay, instead of mutating the system directly. Config you can read back and re-apply beats config that only exists as whatever the last prompt happened to do.
Do I need to know Ansible?
No. The built-in roles are written for you, you mostly edit YAML variables (your git identity, the repositories to clone, the roles to run), the same YAML you already write for Docker Compose, Kubernetes, and CI. Ansible is the engine under the hood; you don't write playbooks to use the collection.
Does it work on macOS or Windows?
Ubuntu, macOS, and WSL. macOS uses the macos role (Homebrew) in place of ubuntu, with ansible_facts.os_family guards on the shared roles; the docker role runs there via colima, and making the runtime a per-host choice is on the roadmap. On Windows you run it inside WSL: the wsl role bridges WSL-side tools into Windows, and the windows role manages the Windows side itself over interop (winget apps, registry, environment variables, virtual desktops, hotkeyed workspace launchers). Running it on Windows without WSL stays out of scope by design.
Do I still need a Brewfile?
No, the vars are your Brewfile. Declare Homebrew taps, formulae, and casks in macos_homebrew_taps, macos_packages, and macos_casks on the macos role, and each apply installs them idempotently, the same job brew bundle does from a Brewfile:
macos_homebrew_taps: # default: []
- hashicorp/tap
macos_packages: # default: [] (formulae; tool roles install their own CLIs)
- jq
- ripgrep
macos_casks: # default: [] (GUI apps and fonts)
- firefox
- visual-studio-codeMigrating from a Brewfile is a one-time move: its tap / brew / cask lines become entries in these three lists. The tool roles install their own CLIs (git, kubectl, the cloud CLIs), so these vars are for the extras on top, your browsers, editors, and fonts.
Can Dotfiles Pro install Mac App Store apps?
Yes. List the numeric app ids in macos_mas_apps and the macos role pulls in mas and installs them, only when the list is non-empty:
macos_mas_apps: # default: []
- 497799835 # Xcode
- 1295203466 # Microsoft Remote DesktopFind an id with mas search <name> (or mas list for what you already have). You must be signed in to the App Store first, and mas installs apps already tied to your account; it can't buy a paid app for the first time.
Can it sync my GUI app settings like Mackup?
Not by copying preference files around. Mackup symlinks an app's settings into a cloud folder; Dotfiles Pro instead declares the settings it manages, so a fresh machine is rebuilt from the declaration rather than from a synced copy. You have three options, in order of preference:
- For macOS preferences that are
defaultskeys, declare them inmacos_defaults, see themacosrole. - For an app whose config is a file you keep in your repo, place it with the
dotfilesrole. - For anything that is neither, keep Mackup alongside; it uses a different mechanism and doesn't clash, so Dotfiles Pro provisions the machine and Mackup restores those last opaque settings.
Will it overwrite my existing config files?
Mostly no. Roles manage the keys they declare (git config, kubeconfig stanzas, .d drop-ins) rather than owning whole files, so a program's own defaults and runtime writes survive, the settings, not files point.
How do I run just one role?
Use tags, ansible-playbook playbook.yaml --tags git, or the dotpro tags <role> command. The common tags are listed under Roles.
How do different machines get different config or roles?
Per-host vars in host_vars/, and a per-host roles_extra list to vary which roles run on each machine. See Configuration.
How do I override or disable a default role alias?
Set the same key in that role's *_aliases dict in your own config. Because the dicts merge (hash_behaviour = merge), you replace just that one alias and keep the rest the role ships:
git_aliases:
status: "git status --short --branch" # replaces the shipped `status`For logic that doesn't fit on one line, point the alias at a function you define in functions (or chain with &&):
git_aliases:
status: "git-status-full"
functions:
git-status-full: |
header 'Git status'
git status --short
git stash listMerging can add or replace a key but never delete one, so to disable an alias you neutralize it rather than remove it, point it at the shell no-op :
git_aliases:
checkout: ":" # typing `checkout` now does nothing instead of `git checkout`Either way it renders into the same ~/.profile.d/aliases/git.sh, so aliases git still lists the alias with your command. The same applies to any role's *_functions dict.
Why does an alias or env var work in one terminal but not another?
Almost always shell startup files, not Dotfiles Pro. Which files a shell reads depends on whether it is a login shell. The collection keeps everything in ~/.profile.d/ and sources it from the login files for your shell: ~/.zprofile and ~/.zshrc for zsh, ~/.profile for bash (the bash role removes ~/.bash_profile so a login bash falls through to ~/.profile).
The gap shows up with bash: an interactive shell that is not a login shell reads only ~/.bashrc, which the framework does not use, so your aliases look missing there. A zsh shell is fine either way, because ~/.zshrc is one of the sourced files. Most terminals and every interactive SSH session start a login shell, so the usual fix is to open a fresh login shell, set your terminal to run the shell as a login shell, or for a one-off run bash -l. A non-login bash shell never sources ~/.profile.d. See the shell configuration pattern.
Where do PATH and environment variables go so every shell sees them?
In ~/.profile.d/. Drop a small *.sh snippet there and the framework sources it from the login files above, so every login shell and SSH session picks it up, the same on Ubuntu and macOS. That is how the roles do it: the unix role adds ~/scripts and ~/.local/bin to PATH with a ~/.profile.d snippet rather than editing ~/.bashrc.
For your own values, add a file under ~/.profile.d/ (link it with the dotfiles role), and keep aliases and functions in the aliases and functions vars, which render under ~/.profile.d/aliases/. One caveat: never write export PATH=$PATH:/dir when $PATH might be empty, the leading colon it produces puts the current directory on your path; guard the directory and prepend to a known value, as the framework's own PATH snippet does. See aliases and functions.
How do I use a different git identity per project?
git_config sets one global identity. For a different name or email under a directory, declare it in the git role's git_configs:
git_configs:
work:
match: gitdir:~/work/ # trailing slash matches everything under it
config:
user:
email: ada@work.example.comThe role renders ~/.config/git/config.work with those keys and adds a matching includeIf to your global config, so git switches identity automatically inside ~/work/. Only the listed keys are overridden, the rest fall through to git_config.
match takes any includeIf condition (gitdir:, onbranch:, …), so the same mechanism applies any config, not just identities. For a one-off in a single repo, run git config user.email ada@work.example.com inside it instead. See the git role.
Can I ship a fixed config file instead of building it from vars?
Yes, the starship and wsl roles each expose two knobs for this. Use whichever fits:
- Verbatim, no Jinja set
starship_config_file(orwsl_config_file) to a file in your playbook'sfiles/dir, or an absolute path. It's copied as-is. - Rendered through Jinja (so it can reference vars) point
starship_config_template(orwsl_config_template) at your own template in your playbook'stemplates/dir.
starship_config_file: starship.toml # copied verbatim
# or
starship_config_template: my-starship.toml.j2 # rendered; default: starship.toml.j2Either way the role's config dict (starship_config / wsl_config) is ignored, and if both are set the *_config_file wins. See the starship and wsl roles.
Don't reuse the shipped template name
For the template path the role's own templates/ is searched before your playbook's, so a file named starship.toml.j2 / wsl.conf.j2 is shadowed by the shipped template. Give yours a different name. The verbatim *_config_file path uses files/, so it has no such clash.
How do I manage secrets?
Never in plain vars, use ansible-vault for vars and the bitwarden role for runtime lookups.
How do I update?
ansible-galaxy collection install -r requirements.yml --upgrade, then re-run the playbook (the dotpro apply command does both). Re-running is idempotent, reinstall and update are the same code path.
Is there a single-binary install like chezmoi?
No, it needs Ansible, which the installer provisions for you (via uv by default, or Ubuntu 24.04+ system packages with DOTPRO_ANSIBLE_METHOD=apt). See Getting started.
How do I stop using it?
Just stop running the playbook. There's no agent or daemon and no symlinks into a source repo, roles write ordinary files and install ordinary packages, so the machine keeps working exactly as last applied. See "Getting out" in Alternatives.