Your first playbook
This playbook prepares a fresh Ubuntu server: baseline settings, Docker, and fail2ban. It needs the installation first.
Inventory
Section titled “Inventory”all: children: servers: hosts: app-1: ansible_host: 203.0.113.10 ansible_user: deployansible_user must be able to use sudo, and its SSH key must work: the base role turns on key-only login.
Playbook
Section titled “Playbook”- hosts: servers become: true vars: # Never banned by fail2ban or CrowdSec. security_allowlist: [192.0.2.0/24, 2001:db8::/32] # Keep the account Ansible connects as; see the gotchas of the base role. base_sshd_allow_users: [deploy] roles: - noixdev.infra.base - noixdev.infra.docker - noixdev.infra.fail2banSet security_allowlist to the networks you manage the server from (your office, your VPN). Replace the example ranges above; they are documentation ranges and match nothing real. The list is the guard against locking yourself out, described in the lockout guard.
Run it
Section titled “Run it”Look at what would change first:
ansible-playbook -i inventory.yml site.yml --check --diffThen apply it:
ansible-playbook -i inventory.yml site.ymlRun it a second time. A role that is idempotent reports changed=0. The collection’s own tests check exactly that for every role.
What you have afterwards
Section titled “What you have afterwards”- Common packages, the time zone set to UTC, unattended security upgrades, and an sshd drop-in at
/etc/ssh/sshd_config.d/50-base.confthat allows key-only login. - Docker CE and the compose plugin, with a
daemon.jsonthat limits log size. - fail2ban banning repeated SSH failures through nftables, with your management networks never banned.
Add more roles
Section titled “Add more roles”Roles go in the order of their dependencies: base and docker first, then the roles that run containers or read their state.
roles: - noixdev.infra.base - noixdev.infra.docker - noixdev.infra.fail2ban - noixdev.infra.crowdsec # after docker, so the bouncer also guards Docker ports - role: noixdev.infra.traefik vars: traefik_acme_email: ops@example.com traefik_dns_env: { HETZNER_API_TOKEN: "{{ vault_dns_token }}" } traefik_cert_domains: [{ main: example.com, sans: ["*.example.com"] }]Keep tokens and passwords in Ansible Vault, as the example does with vault_dns_token. Each role’s page in the guide lists its variables, examples and pitfalls.