Skip to content

Ansible Customization Guide

This document explains how to customize the ansible setup for your dotfiles.

Directory Structure

After chezmoi apply, ansible files are deployed to ~/.ansible/:

~/.ansible/
├── ansible.cfg            # Ansible configuration (sets roles_path, inventory)
├── inventories/
│   └── localhost.ini      # Local inventory
├── playbooks/
│   ├── base.yml           # Cross-platform essentials
│   ├── linux.yml          # Linux-specific setup
│   └── macos.yml          # macOS-specific setup
└── roles/
    ├── base/              # git, curl, ripgrep, fd, etc.
    ├── homebrew/          # macOS Homebrew installation
    ├── neovim/            # Neovim with version check
    └── lazyvim_deps/      # fzf, lazygit, tree-sitter-cli

Prerequisites

Install ansible and required collections:

# Install ansible with uv
uv tool install ansible-core

# Install community.general collection (for homebrew module)
ansible-galaxy collection install community.general

Running Playbooks

Run from ~/.ansible/ directory (ansible.cfg sets inventory and roles_path automatically):

Full Setup

cd ~/.ansible

# macOS
ansible-playbook playbooks/macos.yml

# Linux
ansible-playbook playbooks/linux.yml

Specific Tags

cd ~/.ansible

# Only install neovim
ansible-playbook playbooks/macos.yml --tags neovim

# Install neovim and its dependencies
ansible-playbook playbooks/macos.yml --tags "neovim,lazyvim_deps"

Skip Tags

# Skip tasks requiring sudo (for non-admin users)
ansible-playbook playbooks/linux.yml --skip-tags sudo

Dry Run

# Check what would change without applying
ansible-playbook playbooks/macos.yml --check

# Verbose output
ansible-playbook playbooks/macos.yml --check -v

Available Tags

Tag Description Requires Sudo
base Essential tools (git, curl, ripgrep, fd, jq) Linux only
homebrew macOS Homebrew installation No
neovim Neovim installation with version check Linux only
lazyvim_deps LazyVim dependencies Linux only
networking_tools Networking CLI tools (nmap, mtr, httpie, gping, trippy, etc.) Linux only (partial)
sudo All tasks requiring elevated privileges Yes

Adding New Roles

  1. Create role directory structure:
mkdir -p ~/.ansible/roles/myrole/tasks
mkdir -p ~/.ansible/roles/myrole/defaults  # optional
  1. Create tasks file ~/.ansible/roles/myrole/tasks/main.yml:
---
- name: Install my package (macOS)
  when: ansible_os_family == "Darwin"
  community.general.homebrew:
    name: mypackage
    state: present

- name: Install my package (Debian/Ubuntu)
  when: ansible_os_family == "Debian"
  become: true
  tags: [sudo]
  ansible.builtin.apt:
    name: mypackage
    state: present
  1. Add role to playbook:
# In ~/.ansible/playbooks/macos.yml or linux.yml
roles:
  - role: myrole
    tags: [myrole]

Syncing Changes Back to Chezmoi

After experimenting with changes in ~/.ansible/, add them back to chezmoi:

# Copy modified files back to chezmoi source
cp ~/.ansible/roles/myrole/tasks/main.yml ~/.local/share/chezmoi/dot_ansible/roles/myrole/tasks/main.yml

# Or use chezmoi re-add
chezmoi re-add ~/.ansible/roles/myrole/tasks/main.yml

OS Detection

Ansible facts used for OS detection:

Fact macOS Ubuntu/Debian
ansible_os_family Darwin Debian
ansible_distribution MacOSX Ubuntu
ansible_pkg_mgr homebrew apt

Example conditional:

- name: macOS only task
  when: ansible_os_family == "Darwin"
  # ...

- name: Ubuntu only task
  when: ansible_distribution == "Ubuntu"
  # ...

Sudo Handling

Linux

Most package installations require sudo. Tasks are tagged with sudo:

- name: Install package
  become: true
  tags: [sudo]
  ansible.builtin.apt:
    name: mypackage

Skip these with --skip-tags sudo if you don't have sudo access.

macOS

Homebrew runs as user, no sudo needed. The only exception is system-level changes.

Troubleshooting

Syntax Check

ansible-playbook --syntax-check ~/.ansible/playbooks/base.yml

Verbose Output

ansible-playbook ... -vvv

List Tasks

ansible-playbook ... --list-tasks

List Tags

ansible-playbook ... --list-tags

Common Failures

GLIBC_2.XX not found when running an installed CLI (Ubuntu 22.04 / older distros)

Symptom — a CLI installed by the ansible roles fails at startup:

tv: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.39' not found (required by tv)

Root cause — the .tar.gz or .deb published under the tool's unknown-linux-gnu target was built on a CI image (Ubuntu 24.04 / Debian 13) whose glibc is newer than what the host distro ships. Ubuntu 22.04 LTS is stuck on glibc 2.35, so any binary needing glibc ≥ 2.36 will fail to start.

Fix (already applied in this repo as of the Jammy hardening patch): the ansible roles now prefer musl assets over gnu assets, and fall back to Linuxbrew (or skip-with-warning) when no safe musl asset exists. See docs/linux-package-sources.md → GitHub binary asset selection policy for the full policy.

If you still see the error on a box provisioned by this repo, one of these applies:

  1. A stale gnu binary is still on PATH from a previous apply. The new role only downloads musl / brew when the command -v <tool> check fails — so an old broken binary short-circuits the install. Remove it and re-run chezmoi apply:
rm -f ~/.local/bin/tv /usr/local/bin/tv   # or whichever tool
chezmoi apply
  1. The tool has no upstream musl asset and Linuxbrew isn't installed. Affected tools as of this writing: tv (all arches), git-delta (arm64 only), eza (arm64 only). The role prints an explicit debug message telling you to install Linuxbrew. On Debian/Ubuntu:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
chezmoi apply     # re-runs the role, brew-install branch taken this time
  1. A new tool was added to a role using unknown-linux-gnu again. When adding a role that downloads a Rust/Go binary, prefer the musl asset and fall back to brew-or-skip per the policy linked above. Pattern examples: ripgrep / bat / fd / zellij / tailspin / lnav / trippy.

eza install fails on some hosts with broken third-party apt repo

See the comment on the Add eza repository task in dot_ansible/roles/devtools/tasks/main.yml — the deb.gierens.de repo occasionally ships an expired GPG signature. The role tolerates this and falls through to the GitHub-release fallback, so no manual fix is usually needed. If the fallback also fails, remove the broken repo file:

sudo rm -f /etc/apt/sources.list.d/gierens.list /etc/apt/keyrings/gierens.gpg
sudo apt update
chezmoi apply     # re-runs the role; GitHub-release fallback picks it up

LazyVim Requirements

LazyVim needs:

  • Neovim >= 0.11.2
  • ripgrep (for telescope live grep)
  • fd (for telescope file finder)
  • Node.js (for LSP servers)
  • tree-sitter-cli (for syntax highlighting)
  • lazygit (optional, for git integration)
  • fzf (optional, for fuzzy finding)

All are installed by the base, neovim, and lazyvim_deps roles.