Skip to content

Ansible Overview

Ansible manages everything below the Kubernetes layer: OS configuration, package installation, K3s installation, Tailscale enrollment, and ongoing server management.

Setup

This project uses uv for Python dependency management. All Ansible dependencies (including the ansible package itself) are declared in pyproject.toml.

# Install all dependencies into a local venv
uv sync

# Run any ansible command through uv
uv run ansible <args>
uv run ansible-playbook <args>
uv run ansible-lint

Configuration (ansible.cfg)

[defaults]
stdout_callback = ansible.builtin.default
result_format   = yaml          # YAML output for readability
inventory       = inventory/hosts.yaml
roles_path      = roles

# SOPS vars plugin — decrypts *.sops.yaml inventory vars on the fly
vars_plugins_enabled = host_group_vars,community.sops.sops

host_key_checking    = False
interpreter_python   = auto_silent

The community.sops.sops vars plugin is what makes SOPS-encrypted inventory variables (hosts_secrets.sops.yaml) transparent — Ansible decrypts them automatically before each run.

Inventory Structure

ansible/inventory/
├── hosts.yaml                         # Host and group definitions
├── hosts_secrets.sops.yaml            # Encrypted: ansible_host, ansible_user, SSH keys
└── group_vars/
    ├── k3s_cluster.sops.yml           # k3s version, cluster token, node config
    └── all/
        └── vars.yml                   # Non-secret group variables

Host Groups

Group Members
k3s_cluster all K3s nodes (masters + nodes)
masters k3s-m1, k3s-m2, k3s-oci-m3
nodes (K3s agent nodes — currently empty)
raspberries rpi-4b, rpi-z2w-hyperion
docker rpi-4b
unraid hoarder
hyperion rpi-z2w-hyperion

Collections

Declared in ansible/requirements.yml:

Collection Version Purpose
community.sops 2.2.7 SOPS vars plugin + tasks
community.docker 5.1.0 Docker management on rpi-4b
ansible.posix 2.1.0 POSIX utilities
community.general 12.5.0 General-purpose modules
k3s-io/k3s-ansible 1.2.0 K3s installation and upgrade
artis3n.tailscale 1.2.1 Tailscale server enrollment

Install or update:

uv run ansible-galaxy collection install -r ansible/requirements.yml

Roles

Galaxy roles install to ~/.local/share/ansible/roles (outside the repo). Custom roles live in ansible/roles/ and are committed. The roles_path in ansible.cfg covers both:

uv run ansible-galaxy role install -r ansible/requirements.yml

maintenance Role

Runs on every server via all playbooks (no tags required). Handles:

SSH Hardening (sshd_config.j2)

Deployed to /etc/ssh/sshd_config on every run. Key settings:

Setting Value Purpose
PasswordAuthentication no Keys only
PermitRootLogin no No direct root access
AllowUsers {{ ansible_user }} Only the inventory-defined user per host
MaxAuthTries 3 Disconnect after 3 failed attempts
MaxStartups 3:50:10 Rate-limit pre-auth connections: allow 3, drop 50% above that, hard cap 10
ClientAliveInterval 300 Keepalive every 5 min
ClientAliveCountMax 3 Disconnect after ~15 min idle
AllowTcpForwarding yes Port forwarding (tunnels)
AllowAgentForwarding no Block agent forwarding (hijack risk)
LogLevel VERBOSE Log key fingerprints on auth

Config is validated with sshd -t before the service restarts.

inotify Limits

Sets fs.inotify.max_user_watches and fs.inotify.max_user_instances via sysctl — required for K3s and file-watching tools.

Package Management (tag: update)

Only runs with --tags update. Runs apt dist-upgrade, installs base/group/host packages, cleans up, and reboots if required.

hyperion Role

Installs and manages Hyperion.ng on rpi-z2w-hyperion. Installs via apt, disables the per-user systemd unit, and runs the service as root (hyperion@root.service). Validates port 8090 is reachable after start.

docker_compose Role

Deploys Docker Compose stacks to hosts in the docker group (rpi-4b). Stack definitions live under docker/<inventory_hostname>/ at the repo root (e.g. docker/rpi-4b/adguard/). The role syncs each stack directory to the host, ensures volume directories exist, and manages the compose lifecycle.

Linting

uv run ansible-lint

Pre-commit hook runs ansible-lint automatically on staged playbooks.