A single Ansible playbook works for a few tasks, but grows messy as you automate more. Roles package related automation (installing and configuring Nginx, hardening SSH, setting up a database) into reusable units, with a standard layout. You can apply the same role to many servers and projects, or share it.

Basics first: Ansible playbooks and Ansible inventory.

Create a role

ansible-galaxy role init roles/nginx
roles/nginx/
├── defaults/main.yml      # default variables (lowest priority, meant to be overridden)
├── vars/main.yml          # role variables (high priority; rarely overridden)
├── tasks/main.yml         # the tasks
├── handlers/main.yml      # handlers (restart/reload)
├── templates/             # Jinja2 templates (*.j2)
├── files/                 # static files to copy
├── meta/main.yml          # metadata and dependencies
└── README.md

Fill it in

roles/nginx/defaults/main.yml:

nginx_sites: []
nginx_client_max_body_size: 64m

roles/nginx/tasks/main.yml:

- name: Install nginx
  ansible.builtin.apt:
    name: nginx
    state: present
    update_cache: true
    cache_valid_time: 3600

- name: Main config
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    validate: nginx -t -c %s
  notify: Reload nginx

- name: Site configs
  ansible.builtin.template:
    src: site.conf.j2
    dest: "/etc/nginx/sites-available/{{ item.name }}"
  loop: "{{ nginx_sites }}"
  notify: Reload nginx

- name: Enable sites
  ansible.builtin.file:
    src: "/etc/nginx/sites-available/{{ item.name }}"
    dest: "/etc/nginx/sites-enabled/{{ item.name }}"
    state: link
  loop: "{{ nginx_sites }}"
  notify: Reload nginx

- name: Nginx running
  ansible.builtin.service:
    name: nginx
    state: started
    enabled: true

roles/nginx/handlers/main.yml:

- name: Reload nginx
  ansible.builtin.service:
    name: nginx
    state: reloaded

validate: checks the generated config before replacing the live file, so a bad template never breaks the running server.

Use roles in a playbook

# site.yml
- hosts: web
  become: true
  roles:
    - role: common
    - role: nginx
      vars:
        nginx_sites:
          - { name: example.com, root: /var/www/example.com }
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.yml

Put host- or group-specific values in group_vars/ and host_vars/ instead of the playbook.

defaults vs vars

  • defaults/: values users of the role are expected to override (ports, versions, feature flags).
  • vars/: internal values that shouldn’t normally change (package names per OS).

Ansible’s variable precedence has many levels. Keeping overridable settings in defaults/ avoids surprises.

Reuse community roles and collections

ansible-galaxy collection install community.general community.docker
ansible-galaxy role install geerlingguy.docker

Pin versions in requirements.yml, and review community roles before running them with root privileges:

# requirements.yml
roles:
  - name: geerlingguy.docker
    version: 7.4.1
collections:
  - name: community.general
    version: ">=9.0.0"
ansible-galaxy install -r requirements.yml

Test roles with Molecule

Molecule creates a temporary container or VM, applies the role, runs it again to check idempotency (no changes the second time), and runs verification tests:

pip install molecule molecule-plugins[docker]
cd roles/nginx && molecule init scenario && molecule test

A typical role set for VPS servers

Role Does
common Users, SSH keys, timezone, NTP, base packages, automatic updates
hardening SSH config, firewall, Fail2ban, sysctl. See Ansible server hardening
nginx, php, mysql Web stack
docker Docker Engine + Compose. See Ansible Docker deploy
backup restic schedules. See restic

Quick reference

Task Command / location
New role ansible-galaxy role init roles/x
Tasks / handlers tasks/main.yml, handlers/main.yml
Overridable vars defaults/main.yml
Use in playbook roles: [common, nginx]
Dependencies requirements.yml + ansible-galaxy install -r
Test molecule test

Related: Ansible Vault and Linux configuration management.

  • Ansible
  • DevOps
  • Automation