Ansible Curriculum
Role Basics
Role Components
Advanced Roles
Ansible Galaxy
On this page
Subtopic•Role Basics

What Is A Role

Module 9 covered templates — a way to make configuration files reusable across environments. But what about making entire playbooks reusable? What if you want the same Nginx setup, the same PostgreSQL configuration, or the same user management logic to work identically across multiple projects without copying and pasting?

That is what roles solve. A role is a structured, self-contained package of Ansible content that bundles tasks, handlers, variables, templates, files, and metadata together under one name. Once you have a role, you reference it by name in any playbook, and everything it needs comes with it automatically.

The Problem Roles Solve

Consider a typical playbook for setting up a web server. Without roles, everything lives in one file:

# site.yml - everything in one place
- name: Configure web servers
  hosts: webservers
  become: yes
  vars:
    http_port: 80
    nginx_user: www-data
  tasks:
    - name: Install Nginx
      apt:
        name: nginx
        state: present
    - name: Deploy Nginx config
      template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
    # ... 50 more tasks ...
  handlers:
    - name: Reload Nginx
      service:
        name: nginx
        state: reloaded

This works for one project. But when you start a second project that also needs Nginx, you copy the tasks, handlers, templates, and variables into the new project's playbook. Now you have two copies of the same logic. When you discover a better way to configure something, you have to update both.

Roles break this cycle by making "configure Nginx" a package you reference rather than code you copy.


What a Role Contains

A role is a directory that follows a specific structure. Ansible looks for and automatically loads each of these subdirectories when a role is used:

roles/
└── webserver/            ← this is the role name
    ├── tasks/
    │   └── main.yml      ← the task list (required if role has tasks)
    ├── handlers/
    │   └── main.yml      ← handlers triggered by tasks in this role
    ├── templates/
    │   └── nginx.conf.j2 ← Jinja2 templates used by this role's tasks
    ├── files/
    │   └── index.html    ← static files used by copy/fetch tasks
    ├── vars/
    │   └── main.yml      ← variables that override group_vars/host_vars
    ├── defaults/
    │   └── main.yml      ← default variable values (lowest precedence)
    ├── meta/
    │   └── main.yml      ← role metadata and dependencies
    └── README.md         ← documentation

Only the directories you need must exist — a role with only tasks and handlers does not need a vars/ or templates/ directory.


The Variable Precedence Split: vars/ vs. defaults/

One of the most important things to understand about roles is the distinction between vars/main.yml and defaults/main.yml. They both define variables, but they sit at opposite ends of Ansible's variable precedence hierarchy.

defaults/main.yml — The lowest-priority variable source in all of Ansible. Variables here are overridden by literally anything else: inventory vars, group_vars, host_vars, play vars, extra vars. Use defaults for every variable the role exposes as a tunable setting — port numbers, file paths, feature flags. Anyone using your role can override these simply by defining the variable anywhere else.

vars/main.yml — A high-priority variable source that overrides most other sources. Variables here are constants that the role's internal logic depends on and should not be changed by users. Use vars for things like internal path calculations or OS-specific mappings that the role computes for its own use.

The practical rule: if you want users of your role to be able to customize it, put the variable in defaults/main.yml. If the variable is an internal implementation detail, put it in vars/main.yml.


How Roles Interact with the Playbook Context

When a role runs, its tasks execute in the context of the current host — the same way playbook tasks do. The role's defaults/main.yml variables are merged into the host's variable context at the lowest precedence, meaning any variable the operator defines at the inventory, group, or host level will override the role's defaults automatically.

The role's templates, files, and handlers are also automatically available to the role's tasks without specifying full paths. A task inside the role can reference src: nginx.conf.j2 and Ansible will find it in the role's templates/ directory. A handler defined in the role's handlers/main.yml can be notified from any task in that role.

This self-contained organization is what makes roles portable and shareable. The next section covers the role directory structure in full detail — exactly what goes in each subdirectory and how Ansible loads each one.

←Previous ModuleTemplates