Skip to content

Roles

Scope: packaging a chunk of playbook — tasks, templates, files, variables — into a named, reusable unit with a standard layout. Roles are how Ansible work gets shared between projects and teams.

The problem roles solve

Your "set up a web server" logic lives in one project's playbook. A second project needs it too. Copy-paste gives you two diverging versions and a slow-motion mess. A role is that logic extracted into a bundle with a name: any playbook can declare roles: [webserver] and get the whole behavior, with its variables as the knobs.

The standard layout

ansible-galaxy role init generates the skeleton (run for real in the lab):

roles/demoapp/
├── tasks/main.yml        # the tasks (the only part that's required in practice)
├── handlers/main.yml
├── templates/            # .j2 files, referenced by bare name
├── files/                # files for copy, referenced by bare name
├── vars/main.yml         # role variables (strong — hard to override)
├── defaults/main.yml     # default variables (weak — meant to be overridden)
└── meta/main.yml         # author, description, dependencies on other roles

The demo role's whole job, in examples/roles/demoapp/tasks/main.yml:

---
- name: Role task - drop a marker file
  ansible.builtin.copy:
    content: "installed by the demoapp role\n"
    dest: "/tmp/demoapp-{{ inventory_hostname }}/role-marker.txt"
    mode: "0644"

Using a role

---
- name: Use a role
  hosts: webservers
  roles:
    - demoapp
ansible-playbook roleplay.yml
PLAY RECAP *********************************************************************
web1                       : ok=2    changed=1    unreachable=0    failed=0
web2                       : ok=2    changed=1    unreachable=0    failed=0

changed=1 per host: the marker file landed on both. That's the whole invocation — the role brought its tasks with it, found its own files/templates by name, and blended into the play as if written there.

defaults vs vars: the one subtlety that matters

Both files hold role variables; they differ in precedence (see Variables and facts):

  • defaults/main.yml — weak. Anything anywhere can override them. This is where a role publishes its knobs: "I install nginx with worker_processes=2 unless you say otherwise."
  • vars/main.yml — strong. Overrides most other sources. For the role's internal constants that callers shouldn't casually change.

Putting a knob in vars/ when callers need to tune it is the classic role-authoring mistake; it surfaces as "your variable is being ignored," which is a Troubleshooting entry in every other tool too.

Finding other people's roles

Ansible Galaxy is the public role (and collection) library, and ansible-galaxy install <name> fetches into your project. Treat Galaxy roles like any code you download: read the tasks before running them — a role runs with your SSH credentials and your sudo. Well-kept roles from known authors are a genuine shortcut; abandoned ones are someone else's 2019 opinions frozen in YAML. For a mortal fleet, one more honest option exists: skip roles until a playbook is genuinely reused. Roles are packaging, and packaging before reuse is ceremony.