Getting started
Requirements
- Ubuntu 22.04+, macOS, or WSL
curlandgit
The installer provisions Ansible for you (via uv by default, or Ubuntu 24.04+ system packages with DOTPRO_ANSIBLE_METHOD=apt). For a manual setup you also need Ansible (ansible-core >= 2.16) and optionally Mitogen, both covered below.
You don't need to know Ansible to use the built-in roles, you mostly edit YAML variables (your identity, repos, and which roles to run), the same YAML you already write for Docker Compose, Kubernetes, and CI.
Quick start
Bootstrap a machine from your personal dotfiles repo, it ensures a new-enough Ansible, clones your repo, and installs the collection:
curl -LsSf https://dotfiles.pro/install.sh | sh -s -- git@gitlab.com:yourname/mydotfiles.gitThen review and apply the host with dotpro apply (or add DOTPRO_APPLY=1 to the command above to bootstrap and apply in one shot).
No repo yet? Run it with no argument, it scaffolds a starter in ~/.dotfiles and installs the collection:
curl -LsSf https://dotfiles.pro/install.sh | shEdit playbook.yaml to add your identity, repos, and roles, then apply with dotpro apply.
The argument after -- is your personal repo (the one with playbook.yaml and requirements.yml), see examples/single/ for a starting point.
The installer provisions Ansible through uv on every OS: a current ansible-core with a matched Mitogen in one isolated environment. It pins against a pyproject.toml if your repo ships one (the many starter does, so a fleet resolves the same ansible-core); without it, uv resolves the floor. Set DOTPRO_ANSIBLE_METHOD=apt to use Ubuntu 24.04+ system packages (which ship ansible-core 2.16) instead.
Every apply — the first and every one after — is the dotpro apply command, not this script.
WARNING
Piping a script to a shell runs it with your privileges. Inspect it first with curl -LsSf https://dotfiles.pro/install.sh | less, and pin the collection to a tagged release with version: in requirements.yml.
Manual setup
Prefer to wire it up by hand? Start from examples/single/, three files (requirements.yml, playbook.yaml, ansible.cfg) that keep your config separate from the shared roles:
mkdir -p ~/.dotfiles && cd ~/.dotfiles
# Copy examples/single here, or curl the three starter files:
curl -LsSfO https://dotfiles.pro/examples/single/requirements.yml
curl -LsSfO https://dotfiles.pro/examples/single/playbook.yaml
curl -LsSfO https://dotfiles.pro/examples/single/ansible.cfgInstall Ansible with Mitogen, uv on any OS (what the installer does), or Ubuntu 24.04+ system packages:
# A current ansible-core with a matched Mitogen in one isolated env
curl -LsSf https://astral.sh/uv/install.sh | sh # skip if uv is present
export PATH="$HOME/.local/bin:$PATH"
uv tool install --python 3.14 --with mitogen ansible-core
# Register Mitogen's strategy plugins where Ansible discovers them
sp="$(find "$(uv tool dir)/ansible-core" -type d -path '*/ansible_mitogen/plugins/strategy')"
mkdir -p ~/.ansible/plugins/strategy && ln -sf "$sp"/*.py ~/.ansible/plugins/strategy/
# Modules run on the env's own python (macOS ships no system python3)
export ANSIBLE_PYTHON_INTERPRETER="$(uv tool dir)/ansible-core/bin/python"# A current ansible-core with a matched Mitogen in one isolated env
curl -LsSf https://astral.sh/uv/install.sh | sh # skip if uv is present
export PATH="$HOME/.local/bin:$PATH"
uv tool install --python 3.14 --with mitogen ansible-core
# Register Mitogen's strategy plugins where Ansible discovers them
sp="$(find "$(uv tool dir)/ansible-core" -type d -path '*/ansible_mitogen/plugins/strategy')"
mkdir -p ~/.ansible/plugins/strategy && ln -sf "$sp"/*.py ~/.ansible/plugins/strategy/
# Modules run on the system python (it carries python3-apt for the apt module)
export ANSIBLE_PYTHON_INTERPRETER=/usr/bin/python3sudo apt install ansible ansible-mitogenThe ANSIBLE_PYTHON_INTERPRETER export on the uv path only matters for this first bare run: dotpro apply picks the right module interpreter itself, and the ansible role keeps the toolchain managed from then on.
Then install the collection and apply:
ansible-galaxy collection install -r requirements.yml
ansible-playbook playbook.yaml --ask-become-passPush it to your own git remote to reuse on other machines.
Configuration reference, requirements.yml, playbook.yaml, ansible.cfg
requirements.yml, pulls in the collection:
---
collections:
- name: dotfilespro.rolesplaybook.yaml, lists the roles to run and your vars (identity and repos); --limit $(hostname) applies only the current machine:
---
- name: Configure my machine
hosts: localhost
connection: local
gather_facts: true
diff: true
roles:
# OS base: the matching one runs, the other is skipped (facts decide), so this
# same playbook applies on a Mac or an Ubuntu box. Listed as roles (not a
# templated name) so each role still tags its own tasks for --tags runs.
- role: dotfilespro.roles.ubuntu
when: ansible_facts.os_family == 'Debian'
- role: dotfilespro.roles.macos
when: ansible_facts.os_family == 'Darwin'
- dotfilespro.roles.dotpro
- dotfilespro.roles.ssh
- dotfilespro.roles.git
- dotfilespro.roles.starship
- dotfilespro.roles.docker
- dotfilespro.roles.kubernetes
# Add any other roles you want — see the Roles table in the docs.
# Replace the placeholders below with your own identity and repos.
vars:
git_config:
user:
name: Your Name
email: you@example.com
git_repositories_path: /projects/ # default is ~/
git_repositories:
project: git@gitlab.com:yourname/project.git # cloned to /projects/project
.dotfiles:
repository: git@gitlab.com:yourname/mydotfiles.git
path: ~/.dotfiles # absolute and ~ paths ignore git_repositories_pathansible.cfg (optional, recommended), Mitogen speeds up runs (both install paths above include it; drop the strategy line to run without it); hash_behaviour = merge lets per-host dict vars override only what differs.
[defaults]
# Append a full log of every run to this file, for later auditing
log_path = ~/.ansible/audit.log
# Run tasks through Mitogen for a large speedup over the default executor
strategy = mitogen_linear
# Skip the expensive fact groups (hardware probing, network interface enumeration,
# virtualization, ohai/facter); the roles only read os/distribution, env, user, and
# date facts, so a normal apply gathers far less up front
gather_subset = !hardware,!network,!virtual,!ohai,!facter
# Deep-merge dict vars across files instead of replacing them wholesale
hash_behaviour = merge
# Render task output as readable YAML blocks instead of dense single-line JSON.
# (community.general.yaml was removed in 12.0.0; use the built-in default's
# callback_result_format, the default callback's own option, not result_format.)
stdout_callback = default
callback_result_format = yaml
# Quieter runs: report only changed/failed tasks and their diffs (hide ok/skipped)
display_ok_hosts = False
display_skipped_hosts = False
# A single-host setup plays implicit localhost without an inventory file; that is
# by design, so silence the two warnings pointing it out on every run
localhost_warning = False
[inventory]
inventory_unparsed_warning = FalseThe dotpro commands
The dotpro role installs the dotpro CLI, the day-2 entry point. Add it to your playbook's roles:
roles:
- dotfilespro.roles.dotproThen drive updates from any shell:
dotpro apply # install the collection and apply this host
dotpro tags git # apply a single role
dotpro requirements --upgrade # upgrade the collection onlyThis is what the day-2 loop looks like on a converged machine with a couple of changes pending, a tool upgrade and a new alias (recorded by roles/dotpro/record-demo.sh, so the demo is reproducible):
dotpro is a smart Ansible wrapper
Every dotpro command shells out to plain Ansible, so the raw command is always one step away:
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 --upgradedotpro apply limits to the current host by default; set dotpro_limit_host: false for single-host setups. See the dotpro role for every command and variable.