G
GuideDevOps
Lesson 9 of 14

Roles

Part of the Ansible tutorial series.

The Monolith Problem

When you start learning Ansible, it is common to write massive, 1,000-line playbook files (site.yml) packed with tasks:, vars:, handlers:, and mixed logic for Web Servers, Databases, and Load Balancers all floating in a single document.

This approach is highly problematic because:

  • It is not reusable. You cannot easily take the "Database Configuration" segment and reuse it in another project.
  • It is hard to read. Scrolling through hundreds of tasks is overwhelming.
  • Collaboration is difficult. If a team of five people edits the same site.yml file concurrently, you will face endless Git merge conflicts.

The Solution: Roles

A Role is Ansible's way of bundling automation content into a highly structured, portable, and reusable directory format. It forces you to organize your files (variables, tasks, templates, handlers) into standardized folders.

Instead of a giant playbook, your playbook becomes a simple list of assignments:

# A clean, role-based playbook
---
- name: Deploy Web Infrastructure
  hosts: webservers
  roles:
    - common_security      # Secures the baseline OS
    - nginx                # Installs and configures Nginx
    - nodejs_app           # Deploys the application code
    - datadog_agent        # Installs monitoring agent

Structure of a Role

If you look at the nginx role from the example above, it must adhere to a strict folder structure.

roles/
└── nginx/                  # The role name
    ├── tasks/
    │   └── main.yml        # The core logic (list of steps)
    ├── handlers/
    │   └── main.yml        # Handlers (e.g., Restart Nginx)
    ├── templates/
    │   └── nginx.conf.j2   # Jinja2 templates
    ├── files/
    │   └── static.html     # Raw static files to copy
    ├── vars/
    │   └── main.yml        # High priority variables (internal use)
    ├── defaults/
    │   └── main.yml        # Low priority variables (default overrides)
    └── meta/
        └── main.yml        # Role dependencies and author info

Ansible automatically knows how to load logic from this folder structure because it is standardized.

If you utilize the template module inside roles/nginx/tasks/main.yml, you do not need to provide an absolute path to the .j2 file. Ansible automatically looks inside the templates/ folder of the current role!

# Inside roles/nginx/tasks/main.yml
- name: Deploy Nginx Config
  template:
    src: nginx.conf.j2     # Look ma, no paths!
    dest: /etc/nginx/nginx.conf

Creating a Role Structure Easily

You don't need to manually create all those folders. Use the ansible-galaxy CLI tool to scaffold an empty role framework format.

# Navigate to your roles directory
cd roles/
 
# Generate the scaffold structure
ansible-galaxy init custom_mysql

Creates roles/custom_mysql directory with all the subfolders tasks/, vars/, defaults/, etc., already populated with empty main.yml files.


Defaults vs Variabes (defaults/ vs vars/)

Roles provide variables in two different locations, which often confuses beginners.

defaults/main.yml

This is where you set the default behavior of your role. Variables placed here have the lowest possible precedence in Ansible.

This is intentional! It means whoever consumes your role can effortlessly overwrite these variables in their inventory or playbook without breaking your internal code.

# roles/nginx/defaults/main.yml
nginx_port: 80
nginx_worker_processes: 1

vars/main.yml

Variables placed here have very high precedence. They are meant for internal role logic that the consumer should probably never touch or override.

# roles/nginx/vars/main.yml
nginx_package_name_debian: apache2
nginx_package_name_redhat: httpd

Role Dependencies (Meta)

Sometimes a role requires another role to function. For example, a wordpress role might strictly require a mysql role and a php role.

You declare these requirements inside meta/main.yml:

# roles/wordpress/meta/main.yml
---
dependencies:
  - role: common_security
  - role: php_fpm
  - role: mysql
    # You can even pass variables to dependent roles!
    vars:
      mysql_root_password: "super_secure_default"

When a playbook executes the wordpress role, Ansible reads the meta/main.yml file and automatically runs the three dependency roles before it executes the wordpress tasks.


Using Tags with Roles

When you run a playbook that triggers 5 roles, it might take 10 minutes to execute. If you just changed a single Nginx config file, you don't want to wait 10 minutes.

You can assign Tags to roles in your playbook to execute only specific branches of your automation.

---
- name: Standard Web Tier
  hosts: webservers
  roles:
    - role: common_baseline
      tags: ['base', 'security']
 
    - role: nginx
      tags: ['web', 'frontend']

Now, from the CLI, you can limit execution specifically to the "web" tag:

ansible-playbook site.yml --tags "web"

Ansible will completely skip the common_baseline role and only execute the nginx role, saving massive amounts of compilation time.