Skip to main content
Ansible beginner Lesson 2 of 5

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

Frequently Asked Questions

When should I use a role instead of a playbook?
Use roles when the same configuration logic (e.g., installing nginx, setting up a database) needs to be shared across multiple playbooks or projects. Roles give you a clear, reusable structure.
Where do I store role defaults vs vars?
Put defaults (safe fallbacks) in defaults/main.yml—these are the lowest priority and easy to override. Put role-specific constants in vars/main.yml—these are higher priority and harder to override.