What Are Ansible Roles: Complete Guide to Role Structure (2026)
By Luca Berton · Published 2024-01-01 · Category: installation
Complete guide to Ansible roles. Understand role structure, create reusable roles, use defaults, handlers, templates, and organize playbooks with roles.
Ansible roles are a key feature that simplify and organize complex playbooks, making them reusable and scalable. This article explores what roles are, their structure, and how to use them effectively in your automation workflows.
What Are Ansible Roles?
Roles in Ansible are a way to organize playbooks into reusable components. They allow you to define specific tasks, variables, files, templates, and handlers in a structured format. By using roles, you can easily apply the same configuration across multiple playbooks or projects.
Why Use Roles?
• Code Reusability: Write once and reuse in multiple playbooks. • Simplification: Break down large playbooks into smaller, manageable parts. • Consistency: Maintain uniformity across projects. • Collaboration: Facilitate teamwork by modularizing tasks.See also: Ansible troubleshooting - Error sanity
Structure of an Ansible Role
Ansible roles follow a predefined directory structure, which ensures clarity and organization. Below is the typical structure:
roles/
└── example_role/
├── tasks/ # Task definitions
│ └── main.yml
├── handlers/ # Handlers triggered by tasks
│ └── main.yml
├── templates/ # Jinja2 templates for configuration files
├── files/ # Static files to be copied
├── vars/ # Variables specific to the role
│ └── main.yml
├── defaults/ # Default variable values
│ └── main.yml
├── meta/ # Metadata about the role (dependencies, author info)
│ └── main.yml
└── README.md # Documentation about the role
Key Components:
• tasks/main.yml: Contains the primary tasks for the role. • handlers/main.yml: Defines handlers to be triggered by tasks. • vars/main.yml: Variables with higher precedence. • defaults/main.yml: Default variables with the lowest precedence. • templates/: Stores Jinja2 templates for dynamic configurations. • files/: Holds static files to be copied to target systems.Creating an Ansible Role
To create a role, use the ansible-galaxy command:
ansible-galaxy init my_role
This command creates the necessary directory structure for the role.
Example Task in a Role
Here’s an example of a task to install Nginx in the tasks/main.yml file:
- name: Install Nginx
apt:
name: nginx
state: present
become: yes
Using the Role in a Playbook
Once the role is defined, it can be included in a playbook:
- hosts: all
roles:
- role: my_role
See also: Ansible Molecule: Test Roles & Collections in Containers (Guide)
Best Practices for Ansible Roles
Use Meaningful Names: Name roles descriptively to indicate their purpose. Organize Variables: Usedefaults/ for default values and vars/ for overrides.
Document Roles: Include a README.md to explain the role's functionality.
Leverage Ansible Galaxy: Share and reuse roles via Ansible Galaxy.
Benefits of Ansible Roles
• Scalability: Manage large, complex environments easily. • Flexibility: Modularize configurations for varied requirements. • Maintainability: Simplify updates and troubleshooting.See also: Ansible Collections: What They Are & How to Use Them (2026 Guide)
Conclusion
Ansible roles are a cornerstone of efficient automation, enabling structured, reusable, and scalable playbooks. By adopting roles, you can streamline your workflows and enhance collaboration in DevOps teams.
Explore More About Ansible Roles in Ansible Galaxy
Role Directory Structure
roles/
webserver/
tasks/main.yml # Main task list
handlers/main.yml # Handler definitions
templates/ # Jinja2 templates
files/ # Static files
vars/main.yml # Role variables (high priority)
defaults/main.yml # Default variables (low priority)
meta/main.yml # Dependencies, Galaxy metadata
tests/ # Test playbooks
Create a Role
ansible-galaxy role init webserver
Example Role
# roles/webserver/defaults/main.yml
webserver_port: 80
webserver_docroot: /var/www/html
# roles/webserver/tasks/main.yml
- name: Install nginx
ansible.builtin.package:
name: nginx
state: present
- name: Configure nginx
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: Start nginx
ansible.builtin.service:
name: nginx
state: started
enabled: true
# roles/webserver/handlers/main.yml
- name: restart nginx
ansible.builtin.service:
name: nginx
state: restarted
Use Roles in Playbooks
roles keyword
- hosts: webservers
become: true
roles:
- webserver
- { role: database, db_port: 5432 }
- role: monitoring
when: enable_monitoring | default(true)
include_role (dynamic)
- hosts: all
tasks:
- name: Include role conditionally
include_role:
name: webserver
when: "'webservers' in group_names"
import_role (static)
- hosts: all
tasks:
- import_role:
name: common
tasks_from: security # Run specific task file
Role Dependencies
# roles/webapp/meta/main.yml
dependencies:
- role: common
- role: webserver
webserver_port: 8080
Install from Galaxy
# Install single role
ansible-galaxy role install geerlingguy.docker
# Install from requirements
ansible-galaxy role install -r requirements.yml
# requirements.yml
roles:
- name: geerlingguy.docker
version: "7.1.0"
- name: geerlingguy.nginx
Roles vs Playbooks vs Collections
| Concept | Scope | Sharing | |---------|-------|---------| | Playbook | Complete automation | Project-specific | | Role | Reusable task bundle | Galaxy / Git | | Collection | Roles + modules + plugins | Galaxy / Automation Hub |
FAQ
When should I use a role vs inline tasks?
Use roles when tasks are reusable across projects or complex enough to benefit from organized structure. Simple one-off tasks can stay inline.
What's the variable precedence between defaults and vars?
defaults/main.yml has lowest priority (easily overridden). vars/main.yml has higher priority (harder to override). Use defaults for user-configurable values.
Can I run only part of a role?
- import_role:
name: webserver
tasks_from: configure # Runs tasks/configure.yml
Role Structure
roles/webserver/
├── defaults/main.yml # Default variables (lowest priority)
├── vars/main.yml # Role variables (higher priority)
├── tasks/main.yml # Main task list
├── handlers/main.yml # Handlers (triggered by notify)
├── templates/ # Jinja2 templates
│ └── nginx.conf.j2
├── files/ # Static files
│ └── index.html
├── meta/main.yml # Dependencies, metadata
├── tests/ # Test playbooks
└── README.md
Create a Role
ansible-galaxy role init webserver
Use in Playbook
# Simple
- hosts: webservers
roles:
- webserver
# With variables
- hosts: webservers
roles:
- role: webserver
vars:
http_port: 8080
server_name: example.com
# Conditional
- hosts: all
roles:
- role: webserver
when: install_web | default(true)
defaults/main.yml
# Lowest priority — users can easily override
http_port: 80
server_name: localhost
document_root: /var/www/html
max_connections: 1024
enable_ssl: false
tasks/main.yml
---
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
become: true
- name: Deploy configuration
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
become: true
- name: Start nginx
ansible.builtin.service:
name: nginx
state: started
enabled: true
become: true
handlers/main.yml
---
- name: restart nginx
ansible.builtin.service:
name: nginx
state: restarted
become: true
- name: reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
become: true
meta/main.yml
---
galaxy_info:
author: Your Name
description: Nginx web server role
license: MIT
min_ansible_version: "2.15"
platforms:
- name: Ubuntu
versions: [jammy, noble]
- name: Debian
versions: [bookworm]
dependencies:
- role: common
- role: firewall
vars:
open_ports: [80, 443]
Include Tasks from Role
# In playbook (without full role)
- include_role:
name: webserver
tasks_from: ssl-setup # runs tasks/ssl-setup.yml
Role Dependencies
# meta/main.yml
dependencies:
- role: common
- role: security_baseline
- role: monitoring
vars:
monitor_port: 9090
Organizing Roles
project/
├── ansible.cfg
├── inventory.yml
├── site.yml
├── webservers.yml
├── dbservers.yml
├── group_vars/
├── host_vars/
└── roles/
├── common/ # Shared baseline
├── webserver/ # Nginx/Apache
├── database/ # PostgreSQL/MySQL
├── monitoring/ # Prometheus/Grafana
└── security/ # Hardening
Roles vs include_tasks vs import_tasks
| Feature | Roles | include_tasks | import_tasks | |---------|-------|---------------|--------------| | Reusability | High | Medium | Medium | | Structure | Full directory | Single file | Single file | | Variables | defaults + vars | None | None | | Handlers | Yes | No | No | | Dependencies | Yes | No | No | | Galaxy | Yes | No | No |
FAQ
When should I use a role vs a playbook?
Roles for reusable, shareable automation (install nginx, configure database). Playbooks for orchestration (deploy full stack, run maintenance).
Can I override role defaults?
Yes — set variables in playbook vars, group_vars, host_vars, or extra vars. Role defaults have the lowest priority.
How do I share roles?
Publish to Ansible Galaxy or a private Automation Hub. Or use Git repositories as role sources in requirements.yml.
Related Articles
• discovering content via Ansible Galaxy • template lookups in Ansible • using handlers in Ansible playbooks • become_user and become_method in Ansible • the Ansible Nginx referenceCategory: installation