Ansible Roles: Complete Guide with Real-World Examples
What Are Ansible Roles?
Ansible roles are a way to group related tasks, variables, files, templates, and handlers into a reusable structure. Think of roles as packages of automation logic that can be easily shared and reused across different playbooks and projects.
Why Use Roles?
- Modularity - Break down complex automation into manageable pieces
- Reusability - Write once, use everywhere
- Organization - Clear structure makes code easier to maintain
- Collaboration - Easy to share with team members or the community
- Testing - Isolated components are easier to test
Ansible Role Directory Structure
A standard Ansible role follows this directory structure:
roles/
└── webserver/
├── defaults/ # Default variables (lowest precedence)
│ └── main.yml
├── vars/ # Role variables (high precedence)
│ └── main.yml
├── tasks/ # Main task list
│ └── main.yml
├── handlers/ # Handlers triggered by tasks
│ └── main.yml
├── templates/ # Jinja2 templates
│ └── nginx.conf.j2
├── files/ # Static files to copy
│ └── index.html
├── meta/ # Role metadata and dependencies
│ └── main.yml
└── README.md # Documentation
Creating Your First Role
Use the ansible-galaxy command to create a role skeleton:
ansible-galaxy init webserver
cd webserver
ls -la
Example: Building a Complete Webserver Role
1. Define Default Variables
File: roles/webserver/defaults/main.yml
---
# Default variables for webserver role
http_port: 80
https_port: 443
document_root: /var/www/html
server_name: localhost
# Package names (can be overridden per OS)
web_package: nginx
web_service: nginx
# SSL configuration
enable_ssl: false
ssl_cert_path: /etc/ssl/certs/nginx.crt
ssl_key_path: /etc/ssl/private/nginx.key
2. Create Main Tasks
File: roles/webserver/tasks/main.yml
---
- name: Install web server package
package:
name: "{{ web_package }}"
state: present
become: yes
- name: Ensure document root exists
file:
path: "{{ document_root }}"
state: directory
owner: www-data
group: www-data
mode: '0755'
become: yes
- name: Copy website files
copy:
src: index.html
dest: "{{ document_root }}/index.html"
owner: www-data
group: www-data
mode: '0644'
become: yes
notify: restart webserver
- name: Deploy Nginx configuration
template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/{{ server_name }}.conf
owner: root
group: root
mode: '0644'
become: yes
notify: restart webserver
- name: Enable site configuration
file:
src: /etc/nginx/sites-available/{{ server_name }}.conf
dest: /etc/nginx/sites-enabled/{{ server_name }}.conf
state: link
become: yes
notify: restart webserver
- name: Ensure webserver is running and enabled
service:
name: "{{ web_service }}"
state: started
enabled: yes
become: yes
3. Add Handlers
File: roles/webserver/handlers/main.yml
---
- name: restart webserver
service:
name: "{{ web_service }}"
state: restarted
become: yes
- name: reload webserver
service:
name: "{{ web_service }}"
state: reloaded
become: yes
4. Create Templates
File: roles/webserver/templates/nginx.conf.j2
server {
listen {{ http_port }};
server_name {{ server_name }};
root {{ document_root }};
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
{% if enable_ssl %}
listen {{ https_port }} ssl;
ssl_certificate {{ ssl_cert_path }};
ssl_certificate_key {{ ssl_key_path }};
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
{% endif %}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}
5. Add Metadata
File: roles/webserver/meta/main.yml
---
galaxy_info:
author: Your Name
description: Nginx webserver role
company: Your Company
license: MIT
min_ansible_version: 2.9
platforms:
- name: Ubuntu
versions:
- focal
- jammy
- name: Debian
versions:
- bullseye
- bookworm
galaxy_tags:
- web
- nginx
- webserver
dependencies: []
Using Roles in Playbooks
Basic Usage
---
- name: Deploy web servers
hosts: webservers
roles:
- webserver
With Custom Variables
---
- name: Deploy web servers with SSL
hosts: webservers
roles:
- role: webserver
vars:
server_name: example.com
http_port: 8080
enable_ssl: true
ssl_cert_path: /etc/letsencrypt/live/example.com/fullchain.pem
ssl_key_path: /etc/letsencrypt/live/example.com/privkey.pem
Conditional Role Execution
---
- name: Deploy infrastructure
hosts: all
roles:
- role: webserver
when: "'webservers' in group_names"
- role: database
when: "'databases' in group_names"
Advanced Role Patterns
Role Dependencies
Roles can depend on other roles. Example in meta/main.yml:
dependencies:
- role: common
vars:
ntp_server: pool.ntp.org
- role: firewall
vars:
allowed_ports:
- 80
- 443
Role with Pre and Post Tasks
---
- name: Deploy with pre/post tasks
hosts: webservers
pre_tasks:
- name: Update package cache
apt:
update_cache: yes
become: yes
roles:
- webserver
post_tasks:
- name: Run smoke tests
uri:
url: "http://{{ inventory_hostname }}"
status_code: 200
Dynamic Role Inclusion
---
- name: Deploy based on OS
hosts: all
tasks:
- name: Include OS-specific role
include_role:
name: "webserver-{{ ansible_os_family | lower }}"
Role Best Practices
1. Use Meaningful Names
Choose descriptive role names:
- ✅
nginx-webserver - ✅
postgresql-database - ❌
role1 - ❌
my-stuff
2. Document Everything
Always include a comprehensive README.md:
# Webserver Role
Installs and configures Nginx web server.
## Requirements
- Ansible 2.9+
- Target: Ubuntu 20.04+
## Role Variables
| Variable | Default | Description |
|----------|---------|-------------|
| http_port | 80 | HTTP port |
| server_name | localhost | Server name |
## Example Playbook
```yaml
- hosts: webservers
roles:
- webserver
```
## License
MIT
3. Make Roles Idempotent
Always ensure tasks can run multiple times safely:
# Good - idempotent
- name: Ensure service is running
service:
name: nginx
state: started
# Bad - not idempotent
- name: Start service
command: systemctl start nginx
4. Use Tags for Flexibility
---
- name: Install packages
package:
name: "{{ web_package }}"
state: present
tags:
- install
- packages
- name: Configure service
template:
src: config.j2
dest: /etc/nginx/nginx.conf
tags:
- configure
- config
Run specific tags:
ansible-playbook site.yml --tags "configure"
ansible-playbook site.yml --skip-tags "install"
5. Version Your Roles
Use Git tags for versioning:
git tag v1.0.0
git push origin v1.0.0
Testing Roles
Molecule Framework
Use Molecule for comprehensive role testing:
# Install Molecule
pip install molecule molecule-docker
# Initialize Molecule in role
cd roles/webserver
molecule init scenario
# Test role
molecule test
Kitchen-CI
Alternative testing with Test Kitchen:
# .kitchen.yml
---
driver:
name: docker
provisioner:
name: ansible_playbook
roles_path: roles
playbook: test.yml
platforms:
- name: ubuntu-22.04
suites:
- name: default
Sharing Roles via Ansible Galaxy
1. Prepare Your Role
# Ensure meta/main.yml is complete
# Add README.md with documentation
# Add LICENSE file
# Create git repository
2. Publish to Galaxy
# Login to Ansible Galaxy
ansible-galaxy login
# Import role from GitHub
ansible-galaxy role import yourusername your-role-repo
3. Install from Galaxy
# Install specific role
ansible-galaxy install geerlingguy.nginx
# Install from requirements.yml
ansible-galaxy install -r requirements.yml
Common Role Patterns
Multi-OS Support
---
# vars/Debian.yml
web_package: nginx
web_service: nginx
# vars/RedHat.yml
web_package: nginx
web_service: nginx
# tasks/main.yml
- name: Include OS-specific variables
include_vars: "{{ ansible_os_family }}.yml"
- name: Install web server
package:
name: "{{ web_package }}"
state: present
Environment-Specific Configuration
# group_vars/production.yml
http_port: 80
enable_ssl: true
# group_vars/development.yml
http_port: 8080
enable_ssl: false
Troubleshooting Roles
Debugging Tips
# Verbose output
ansible-playbook site.yml -vvv
# Check syntax
ansible-playbook site.yml --syntax-check
# Dry run
ansible-playbook site.yml --check
# List all tasks
ansible-playbook site.yml --list-tasks
Conclusion
Ansible roles are essential for building scalable, maintainable automation. By following the patterns and best practices in this guide, you'll create roles that are:
- Easy to understand and maintain
- Reusable across projects
- Well-documented and tested
- Ready to share with the community
Start small with simple roles, then gradually incorporate advanced patterns as your needs grow. The modular nature of roles will transform how you approach infrastructure automation.
Ready to Practice?
Try creating your own role in our Interactive Playground or explore more automation patterns in our Learning Center.