Side reading

Ansible's documentation is written for people managing hundreds of machines. You have one. This is the subset that matters, and what the words mean.

The vocabulary #

Inventory: which machines exist. A YAML file listing hosts and variables. Yours has one host.

Play: a set of tasks applied to a set of hosts.

Playbook: a file containing one or more plays.

Task: one thing that should be true. Calls a module.

Module: the code that knows how to make something true and how to check whether it already is. ansible.builtin.copy, community.general.ufw.

Handler: a task that runs only if another task reported a change. Reloading a service after its config file changed.

Role: a directory structure that packages tasks, files and variables together. Useful past a few hundred lines; skip it until then.

Collection: a distributable bundle of modules. ansible.builtin ships with Ansible; anything else you install.

Fact: something Ansible discovered about the machine. Gathered automatically at the start of a play.

Idempotent: running it twice does what running it once did. The property the whole tool exists to provide.

What you actually need #

One inventory, one playbook, a files/ directory. That is a complete home server setup and it will stay readable at a few hundred lines.

homeserver/
  inventory.yml
  site.yml
  files/
    10-hardening.conf
    52-upgrades

Add roles when site.yml becomes hard to navigate, not before. Splitting three tasks into a role structure is ceremony.

The modules worth knowing #

Nine of them cover nearly everything a home server needs:

Module For
ansible.builtin.apt packages
ansible.builtin.copy a file whose content you have
ansible.builtin.template a file with variables in it
ansible.builtin.file directories, permissions, symlinks
ansible.builtin.service starting, enabling, reloading
ansible.builtin.systemd_service when you need timers or a daemon-reload
ansible.builtin.user accounts and group membership
ansible.builtin.lineinfile one line in a file you do not own
community.general.ufw firewall rules

Prefer copy or template over lineinfile when you own the file. Owning the whole file means you know what is in it; editing lines in place means the result depends on what was there before.

The flags worth knowing #

$ ansible-playbook -i inventory.yml site.yml --check --diff
$ ansible-playbook -i inventory.yml site.yml --limit homeserver
$ ansible-playbook -i inventory.yml site.yml --tags firewall
$ ansible-playbook -i inventory.yml site.yml -v

--check --diff is the important one. Run it before every real run until you trust the playbook, and after that whenever the change touches something that could lock you out.

Things that catch people #

YAML indentation. Two spaces, never tabs. Most "syntax error" messages are a mis-indented line several lines above where it points.

become is not automatic. Add become: true at the play level and tasks run through sudo.

Reported changes that are not changes. A task that says changed every run is not idempotent. Usually command or shell, which cannot know whether the work was needed. Add a creates: or a when:, or find a module that does the job properly.

command versus shell. command does not use a shell, so no pipes or redirection, which is why it is safer. shell gives you a shell. Reach for a real module before either.

Handlers only run at the end of a play, and only if notified. If you need something reloaded immediately, meta: flush_handlers forces it.

Templates use Jinja2, the same syntax as {{ variable }} elsewhere. A .j2 file with variables, rendered on the target.

Where variables live #

More places than you need. For one machine, two are enough:

  • In the inventory, next to the host or under vars:. Good for anything machine-specific.
  • In the playbook, under vars:. Good for things the playbook itself needs.

Ignore the precedence rules until you have a conflict. There are twenty-two levels and you will not encounter most of them.

What not to bother with #

Ansible Vault, if you already use SOPS. Two secret systems is one too many.

Dynamic inventory. For one host, a static file is correct.

AWX or Tower. A web interface for running playbooks, aimed at teams.

Testing frameworks. Molecule tests roles across distributions. Your test is --check and one machine.

When Ansible is the wrong tool #

It configures a host. It is not good at deploying applications, which is what compose is for, and it is not good at anything requiring conversation with a running system.

The split this book uses: Ansible owns the host, compose owns the containers. Keep that line clear and both stay simple.

Settings

Your values

The book is written with placeholder names so it makes sense to everybody. Put your own in and every chapter, every command and every copy-paste prompt updates to match.

Nothing here is sent anywhere. It is saved in this browser, so it comes back next time. A different browser or a private window gets the placeholders again.

Live preview

$ ssh admin@192.168.1.20
$ sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp
$ sudo hostnamectl set-hostname homeserver
$ sudo timedatectl set-timezone Europe/Paris

Real commands from chapters 2, 3 and 4. They change as you type.

The account you log in as. Not root, and not necessarily the same name you use on your laptop.

Introduced in Chapter 2, Meet your server

The book's placeholder is admin

What the machine calls itself. You choose it, and it shows up in your shell prompt and your logs.

Introduced in Chapter 3, A safe front door

The book's placeholder is homeserver

The IP address your server has on your home network, from ip -brief addr.

Introduced in Chapter 2, Meet your server

The book's placeholder is 192.168.1.20

The address range and prefix shown by ip route or ip -brief addr, written in CIDR form. Copy the real prefix; do not guess /24.

Introduced in Chapter 2, Meet your server

The book's placeholder is 192.168.1.0/24

The address traffic goes to on its way out of your house, from ip route.

Introduced in Chapter 2, Meet your server

The book's placeholder is 192.168.1.1

In Region/City form, or Etc/UTC if you would rather read logs in UTC.

Introduced in Chapter 3, A safe front door

The book's placeholder is Europe/Paris

A registered name you control. Chapter 8 uses it for the LAN route; chapter 12 uses a separate private Tailscale name remotely.

Introduced in Chapter 8, One door, many rooms

The book's placeholder is example.com

The email identity allowed to administer the tagged server in your Tailscale policy.

Introduced in Chapter 10, Your own private network

The book's placeholder is you@example.com

The mailbox that should receive actionable home-server alerts.

Introduced in Chapter 14, Knowing it is alive

The book's placeholder is alerts@example.com

Once you save, the prose and the commands read with your names, the copy buttons copy your values, and the copy-paste prompts describe your machine accurately. That last one matters: an assistant told your network is 192.168.1.0/24 when it is not will send you chasing the wrong thing.

Anything you leave empty keeps the book's placeholder.