Managing packages and configuration files with Ansible and Chezmoi
The Pros and Cons of Arch Linux
My Linux distro of choice for my main computer is Arch Linux (btw.™). I love its approach of being lightweight and shipping the latest software with (almost) default configuration as intended by the developers. For me this is perfect as I like to try out new software and I am also forced to configure some packages myself. The ArchWiki is a great resource helping me on my learning journey. There is also the large Arch User Repository (AUR) where even more software packages can be found. Personally, I don't need any packages from the AUR at the moment but it's nice to have the option.
As with most things in life there are tradeoffs of using a rolling release distro like Arch. The benefit of having access to the latest software comes with the occasional bug or breaking change where user intervention is necessary. Sometimes, a clean reinstall is the easiest way to get up and running again. Wouldn't it be cool to replicate the configuration automatically? That's what this article is about.
Package Management with Ansible
Ansible is an automation tool for managing system configurations mainly on remote machines via SSH but it can also be used locally. By using playbooks you can declare the desired state you want the system to be in. Ansible will check the current state and will only perform actions when necessary. This means that playbooks are idempotent in the general case so running it twice is the same as running it once.
Setting up Ansible
The first configuration step is to create a inventory.ini file within a project directory where we tell Ansible to execute the playbook locally.
[localhost]
127.0.0.1 ansible_connection=local
The (optional) second step is to configure default parameters in the ansible.cfg file. This saves you from specifying the options -K -i inventory.ini every time you run a playbook.
[defaults]
inventory = inventory.ini
[privilege_escalation]
become_ask_pass = trueWriting a Playbook
Ansible playbooks are defined as YAML files which can be executed with ansible-playbook playbook.yaml. We start by giving the play a name and referencing the localhost group from the inventory. become: true ensures privileges are escalated by default for all tasks.
---
- name: Arch Linux setup
hosts: localhost
become: trueInstalling regular Packages
Regular packages can simply be installed by listing their names below the pacman module.
tasks:
- name: Install packages
community.general.pacman:
name:
- kitty
- fish
- zoxide
- starshipEnabling Systemd Services
Some packages need a systemd service running in the background, e.g. Ollama1. They can be enabled using the systemd_service module. I use block statements to organize related tasks into groups.
- name: Setup Ollama
block:
- name: Install Ollama
community.general.pacman:
name:
- ollama
- ollama-cuda
- name: Enable ollama.service
ansible.builtin.systemd_service:
name: ollama
enabled: trueManaging Users and Groups
Other packages need the user to be in a certain group in order to work properly, e.g. Wireshark2. Group memberships can be managed with the user module. The task below also uses a template to dynamically determine the user name on execution.
- name: Setup Wireshark
block:
- name: Install Wireshark
community.general.pacman:
name: wireshark-qt
- name: Add to wireshark group
ansible.builtin.user:
name: "{{ lookup('ansible.builtin.env', 'USER') }}"
groups:
- wireshark
append: trueConfiguration Management with Chezmoi
Chezmoi is a tool called a dotfile manager. It keeps track of user configuration files e.g. in the ~/.config directory.
Configuring Fish
Fish is a great shell for interactive use. It has builtin syntax highlighting and suggestions based on a program's man pages. Recently, its core has been rewritten in Rust. Using it as the default shell can cause problems because fish is not POSIX compliant.3 So instead I configured my terminal emulator Kitty to drop into fish on startup using this line in ~/.config/kitty/kitty.conf.
shell fish
Next, I set up zoxide, a cd replacement, and Starship, a shell promt, in ~/.config/fish/config.fish.
if status is-interactive
# Commands to run in interactive sessions can go here
zoxide init fish | source
starship init fish | source
end
To initialize chezmoi, execute chezmoi init. Both config files can now be added to chezmoi using chezmoi add [file]. To check the files tracked by chezmoi into git, switch to its source directory using chezmoi cd and add, commit, and push them to a repository.
You can now replicate the configuration on a new machine using the command:
chezmoi init --apply [repo]