Ansible Roles (Reusable Configuration Units)
Learn how to structure Ansible code using roles: directory layout, tasks, handlers, templates, defaults, and how to share roles across projects.
A role is Ansible’s way of packaging reusable automation logic. Instead of one big playbook, roles split tasks, templates, variables, and handlers into a conventional directory structure you can share and reuse.
Learning outcomes
By the end you can:
- create a role with the standard directory layout
- use role defaults and variables
- call a role from a playbook
- understand how handlers work inside roles
1) Why roles?
Without roles, playbooks grow large and hard to maintain. Roles solve this by providing a standard folder structure so Ansible knows where to find each piece automatically.
my_role/
├── tasks/
│ └── main.yml # What to do
├── handlers/
│ └── main.yml # What to do when notified
├── templates/
│ └── nginx.conf.j2 # Jinja2 templates
├── files/
│ └── index.html # Static files to copy
├── defaults/
│ └── main.yml # Default variable values (lowest priority)
├── vars/
│ └── main.yml # Role variables (higher priority)
└── meta/
└── main.yml # Role metadata and dependencies
Ansible auto-loads each of these directories—you never need to explicitly import them.
2) Create a role with ansible-galaxy
Use the built-in scaffolding command:
ansible-galaxy role init nginx_role
This creates the full directory structure for you.
3) Role tasks (tasks/main.yml)
---
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Deploy nginx configuration
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
notify: Reload nginx
- name: Ensure nginx is started and enabled
ansible.builtin.service:
name: nginx
state: started
enabled: true
4) Role handlers (handlers/main.yml)
Handlers run only when notified—perfect for restarting services after config changes.
---
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
5) Role defaults (defaults/main.yml)
Defaults are safe fallbacks. Any playbook or inventory variable overrides them.
---
nginx_port: 80
nginx_worker_processes: auto
nginx_worker_connections: 1024
6) Jinja2 template (templates/nginx.conf.j2)
Templates use {{ variable }} syntax to inject values at run time.
worker_processes {{ nginx_worker_processes }};
events {
worker_connections {{ nginx_worker_connections }};
}
http {
server {
listen {{ nginx_port }};
root /var/www/html;
index index.html;
}
}
7) Call the role from a playbook
---
- name: Configure web servers
hosts: web
become: true
roles:
- role: nginx_role
vars:
nginx_port: 8080
Ansible resolves role tasks, handlers, and templates automatically based on the folder name.
8) Role with multiple tasks files (include pattern)
For complex roles, split tasks into multiple files and include them:
# tasks/main.yml
---
- name: Include install tasks
ansible.builtin.include_tasks: install.yml
- name: Include configure tasks
ansible.builtin.include_tasks: configure.yml
- name: Include service tasks
ansible.builtin.include_tasks: service.yml
# tasks/install.yml
---
- name: Install nginx package
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
9) Galaxy roles (community roles)
You can install community roles from Ansible Galaxy:
ansible-galaxy install geerlingguy.nginx
Then reference in a requirements.yml:
---
roles:
- name: geerlingguy.nginx
version: "3.2.0"
Install all requirements:
ansible-galaxy install -r requirements.yml
Next steps
- Variables and Vault: managing secrets in Ansible
- Advanced playbook patterns: loops, conditionals, error handling
- Running Ansible in CI/CD pipelines