Roles
Each role's page is its own README, what it does, variables, and aliases. Roles are grouped by purpose below; on disk they stay flat under roles/.
Overview
| Role | Description |
|---|---|
| Dotfiles Pro | |
| Ansible | Ansible itself, and applies this host's roles_extra |
| dotpro | The dotpro CLI for the install/apply workflow |
| dotfiles | Link, copy, or template raw dotfiles into $HOME, with backup and pruning |
| System | |
| Unix | OS-neutral base every platform role imports: ~/.profile.d/ framework, generic aliases |
| Ubuntu | System updates, locale, ~/.profile.d/ shell framework, bash aliases |
| macOS | System setup via Homebrew, macOS defaults and Dock; imports the unix shell framework |
| WSL | wslu utilities, wsl.conf and .wslconfig, WSL-specific aliases |
| Windows | Windows itself, managed from WSL: winget packages, registry, env vars, virtual desktops, workspace launchers |
| Shell | |
| Bash | The bash shell role, and the shared alias / script / version-recording generator other roles import |
| Zsh | The zsh shell role (completions via compinit); unix selects it on macOS |
| Nerd Fonts | Nerd Fonts (FiraCode) installed system-wide |
| Starship | starship.rs prompt with k8s/terraform/docker/AWS context |
| Development | |
| Git | Git config, pre-commit, 20+ git aliases, repository cloning |
| Projects | Per-project jump aliases and a project/projects command, rendered from the projects map |
| GitHub | gh CLI, PR/issue/run aliases |
| GitLab | glab CLI, MR/issue/pipeline aliases |
| Python | Python tooling |
| Sentry | sentry-cli, project/issue/release aliases |
| Infrastructure | |
| Docker | Docker and Docker Compose |
| Kubernetes | kubectl, kubeseal, k9s and 25+ aliases |
| OpenTofu | OpenTofu CLI and 20+ aliases |
| Terraform | Terraform CLI |
| Cloud | |
| AWS | AWS CLI and AWSume |
| DigitalOcean | doctl CLI |
| Google Cloud | Google Cloud CLI |
| Google Workspace | gws CLI for Gmail, Drive, and Calendar, with mailbox aliases |
| AI | |
| Agents | AGENTS.md and ~/.agents/worklog scaffolding for coding agents |
| Claude | Claude CLI |
| Security | |
| Bitwarden | Bitwarden CLI |
| SSH | Keychain-based SSH key loading |
Tags
Every task carries its role name, plus a few cross-cutting tags, so --tags can target partial runs (the dotpro subcommands wrap the common ones):
| Tag | Selects | Alias |
|---|---|---|
<role>, e.g. git, kubernetes | everything in that role | dotpro tags <role> |
config | configuration tasks only, skips package installs | dotpro tags config |
aliases | shell alias / function generation | dotpro tags aliases |
repositories | cloning the repos in git_repositories | — |
dotfiles | placing and reconciling the files in dotfiles_list (the dotfiles role) | dotpro dotfiles status |
dock | shaping the Dock from macos_dock_persist / macos_dock_remove (the macos role) | — |
Combine them with a comma (--tags git,config). config is the fast path for re-applying settings without re-checking every package.
dotpro is a smart Ansible wrapper
The dotpro subcommands above are thin wrappers over --tags runs of plain Ansible:
dotpro apply # ansible-playbook playbook.yaml --ask-become-pass
dotpro plan # ansible-playbook playbook.yaml --check --diff
dotpro tags git # ansible-playbook playbook.yaml --tags git
dotpro tags # ansible-playbook playbook.yaml --list-tags
dotpro vars # ansible-inventory --host $(hostname) --yaml
dotpro requirements --upgrade # ansible-galaxy collection install -r requirements.yml --upgradeCustom roles
Adding your own roles takes one directory: place them under roles/ in your personal repository and reference them by short name in playbook.yaml. They are picked up automatically alongside the collection roles. The local roles/ directory is separate from the collection's name: your roles are called by short name (myrole), the collection's by their dotted name (dotfilespro.roles.git), so a bare name always resolves to your local role and the two never collide.
To share roles with others, publish them as a collection and install it next to this one, see Plugins for what a plugin requires and the index of known plugins.
~/.dotfiles/
requirements.yml
playbook.yaml
ansible.cfg
roles/ ← optional: your own custom rolesCustom tasks
Adding your own tasks needs no new machinery: playbook.yaml is plain Ansible, so a play takes more than roles:. You can add one-off steps you don't want to package as a role, and they run with the same connection: local, facts, and vars as the roles around them.
A play runs its sections in a fixed order, not the order they appear in the file:
| Section | Runs |
|---|---|
pre_tasks: | before the roles, for setup a role depends on |
roles: | the roles themselves |
tasks: | after the roles, the usual place for your own steps |
post_tasks: | last, after everything else |
So a tasks: block at the bottom of the play still runs after every role, and a pre_tasks: block is the only way to run something before them. Handlers notified in one section flush at the end of that section, before the next begins.
---
- name: Configure my machine
hosts: all
connection: local
roles:
# OS base: the matching one runs, the other is skipped (facts decide)
- role: dotfilespro.roles.ubuntu
when: ansible_facts.os_family == 'Debian'
- role: dotfilespro.roles.macos
when: ansible_facts.os_family == 'Darwin'
- dotfilespro.roles.git
post_tasks:
- name: Symlink a local config the roles don't manage
ansible.builtin.file:
src: "{{ playbook_dir }}/files/myapp.conf"
dest: ~/.config/myapp/config
state: link
tags: myappTag your tasks so --tags still targets them, and gate machine-specific steps with when: on a host fact or an inventory var. Anything Ansible can do is fair game here: any module from a collection in requirements.yml, a block: with become: true for a privileged step, or your own custom modules and action plugins dropped under library/ and action_plugins/ next to the playbook.
For more than a couple of tasks, or anything you'd reuse across machines, promote them into a role under roles/ instead, see Custom roles.