Ansible Roles - Organizing Your Automation
Status: Active
Last Updated: 2026-01-30
Category: Infrastructure - Configuration Management
Prerequisites: ansible-playbooks, ansible-inventory
Time: 3-4 hours
Tags: ansible, roles, organization, reusability, galaxy
Summary
Master Ansible roles to create reusable, maintainable automation code. Learn role structure, dependencies, variables, defaults, and how to leverage Ansible Galaxy for community roles. Build production-ready role-based infrastructure automation.
๐ฏ What You'll Learn
By the end of this article, you'll be able to:
- โ Understand role structure and components
- โ Create custom roles
- โ Use role variables and defaults
- โ Manage role dependencies
- โ Share roles via Ansible Galaxy
- โ Use community roles effectively
- โ Organize complex playbooks with roles
๐ค Why Roles?
Without Roles (Messy!)
# site.yaml - 1000+ lines, hard to maintain
- name: Configure everything
hosts: webservers
tasks:
- name: Install Nginx
apt: ...
- name: Configure Nginx
template: ...
- name: Install PostgreSQL
apt: ...
- name: Configure PostgreSQL
template: ...
# ... 100 more tasks
With Roles (Clean!)
# site.yaml - clean and organized
- name: Configure web servers
hosts: webservers
roles:
- common
- nginx
- postgresql
- monitoring
Benefits:
- โ Reusable across playbooks
- โ Shareable with team/community
- โ Easier to test
- โ Clear organization
- โ Version controllable
๐ Role Structure
Standard Role Directory
roles/
โโโ nginx/
โโโ tasks/
โ โโโ main.yaml # Main tasks
โโโ handlers/
โ โโโ main.yaml # Handlers (service restarts, etc.)
โโโ templates/
โ โโโ nginx.conf.j2 # Jinja2 templates
โโโ files/
โ โโโ index.html # Static files
โโโ vars/
โ โโโ main.yaml # Variables (high priority)
โโโ defaults/
โ โโโ main.yaml # Default variables (low priority)
โโโ meta/
โ โโโ main.yaml # Role metadata and dependencies
โโโ tests/
โ โโโ inventory # Test inventory
โ โโโ test.yaml # Test playbook
โโโ README.md # Documentation
Only create directories you need! Empty directories are fine to skip.
๐๏ธ Creating Your First Role
Generate Role Skeleton
# Create role structure
ansible-galaxy init roles/nginx
# Or in specific directory
ansible-galaxy init --init-path roles nginx
Generated structure:
roles/nginx/
โโโ README.md
โโโ defaults/
โ โโโ main.yml
โโโ files/
โโโ handlers/
โ โโโ main.yml
โโโ meta/
โ โโโ main.yml
โโโ tasks/
โ โโโ main.yml
โโโ templates/
โโโ tests/
โ โโโ inventory
โ โโโ test.yml
โโโ vars/
โโโ main.yml
Build Nginx Role
roles/nginx/defaults/main.yaml:
---
# Default variables (can be overridden)
nginx_port: 80
nginx_worker_processes: auto
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_sites:
- name: default
template: default.conf.j2
enabled: true
roles/nginx/vars/main.yaml:
---
# High-priority variables (harder to override)
nginx_user: www-data
nginx_config_dir: /etc/nginx
nginx_log_dir: /var/log/nginx
roles/nginx/tasks/main.yaml:
---
- name: Install Nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Create config directory
file:
path: "{{ nginx_config_dir }}/sites-available"
state: directory
mode: '0755'
- name: Configure Nginx main config
template:
src: nginx.conf.j2
dest: "{{ nginx_config_dir }}/nginx.conf"
validate: 'nginx -t -c %s'
notify: Restart Nginx
- name: Configure sites
template:
src: "{{ item.template }}"
dest: "{{ nginx_config_dir }}/sites-available/{{ item.name }}"
loop: "{{ nginx_sites }}"
when: item.enabled | default(true)
notify: Reload Nginx
- name: Enable sites
file:
src: "{{ nginx_config_dir }}/sites-available/{{ item.name }}"
dest: "{{ nginx_config_dir }}/sites-enabled/{{ item.name }}"
state: link
loop: "{{ nginx_sites }}"
when: item.enabled | default(true)
notify: Reload Nginx
- name: Start and enable Nginx
service:
name: nginx
state: started
enabled: yes
roles/nginx/handlers/main.yaml:
---
- name: Restart Nginx
service:
name: nginx
state: restarted
- name: Reload Nginx
service:
name: nginx
state: reloaded
- name: Test Nginx config
command: nginx -t
changed_when: false
roles/nginx/templates/nginx.conf.j2:
user {{ nginx_user }};
worker_processes {{ nginx_worker_processes }};
pid /run/nginx.pid;
events {
worker_connections {{ nginx_worker_connections }};
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout {{ nginx_keepalive_timeout }};
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log {{ nginx_log_dir }}/access.log;
error_log {{ nginx_log_dir }}/error.log;
gzip on;
include /etc/nginx/sites-enabled/*;
}
roles/nginx/templates/default.conf.j2:
server {
listen {{ nginx_port }};
server_name _;
root /var/www/html;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
roles/nginx/meta/main.yaml:
---
galaxy_info:
author: Your Name
description: Nginx web server role
company: Your Company
license: MIT
min_ansible_version: '2.9'
platforms:
- name: Ubuntu
versions:
- focal
- jammy
- name: Debian
versions:
- bullseye
galaxy_tags:
- nginx
- web
- webserver
dependencies: []
Use the Role
playbook.yaml:
---
- name: Configure web servers
hosts: webservers
become: yes
roles:
- nginx
With variable overrides:
---
- name: Configure web servers
hosts: webservers
become: yes
roles:
- role: nginx
vars:
nginx_port: 8080
nginx_sites:
- name: mysite
template: mysite.conf.j2
enabled: true
๐ Role Dependencies
Define Dependencies
roles/webapp/meta/main.yaml:
---
dependencies:
- role: common
- role: nginx
vars:
nginx_port: 80
- role: postgresql
vars:
postgres_version: 15
When webapp role runs, it automatically includes common, nginx, and postgresql roles first.
Conditional Dependencies
dependencies:
- role: ssl
when: ssl_enabled | bool
- role: monitoring
when: env == "production"
๐ฆ Multiple Roles Example
Project structure:
ansible-project/
โโโ ansible.cfg
โโโ inventory/
โ โโโ hosts.yaml
โโโ playbooks/
โ โโโ site.yaml
โ โโโ webservers.yaml
โ โโโ databases.yaml
โโโ roles/
โโโ common/
โโโ nginx/
โโโ postgresql/
โโโ monitoring/
โโโ security/
Common Role
roles/common/tasks/main.yaml:
---
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install common packages
apt:
name:
- curl
- wget
- vim
- git
- htop
- tmux
state: present
- name: Configure timezone
timezone:
name: "{{ timezone | default('UTC') }}"
- name: Configure NTP
apt:
name: systemd-timesyncd
state: present
- name: Start timesyncd
service:
name: systemd-timesyncd
state: started
enabled: yes
- name: Create admin users
user:
name: "{{ item.name }}"
groups: "{{ item.groups | default([]) }}"
shell: /bin/bash
state: present
loop: "{{ admin_users | default([]) }}"
- name: Add SSH keys for admins
authorized_key:
user: "{{ item.name }}"
key: "{{ item.ssh_key }}"
state: present
loop: "{{ admin_users | default([]) }}"
when: item.ssh_key is defined
Security Role
roles/security/tasks/main.yaml:
---
- name: Install fail2ban
apt:
name: fail2ban
state: present
- name: Configure fail2ban
template:
src: jail.local.j2
dest: /etc/fail2ban/jail.local
notify: Restart fail2ban
- name: Configure UFW defaults
ufw:
direction: "{{ item.direction }}"
policy: "{{ item.policy }}"
loop:
- { direction: 'incoming', policy: 'deny' }
- { direction: 'outgoing', policy: 'allow' }
- name: Allow SSH
ufw:
rule: allow
port: "{{ ssh_port | default('22') }}"
proto: tcp
- name: Allow HTTP/HTTPS
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- '80'
- '443'
when: allow_web | default(false)
- name: Enable UFW
ufw:
state: enabled
- name: Disable root SSH login
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PermitRootLogin'
line: 'PermitRootLogin no'
notify: Restart SSH
- name: Disable password authentication
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?PasswordAuthentication'
line: 'PasswordAuthentication no'
notify: Restart SSH
when: disable_password_auth | default(false)
Site Playbook
playbooks/site.yaml:
---
- name: Configure all servers
hosts: all
become: yes
roles:
- common
- security
- name: Configure web servers
hosts: webservers
become: yes
roles:
- nginx
- monitoring
- name: Configure database servers
hosts: databases
become: yes
roles:
- postgresql
- monitoring
๐ Ansible Galaxy
Ansible Galaxy: Public repository of community roles.
Search Galaxy
# Search for roles
ansible-galaxy search nginx
# Search with filters
ansible-galaxy search nginx --platforms Ubuntu
# Search by author
ansible-galaxy search --author geerlingguy
Install Role from Galaxy
# Install role
ansible-galaxy install geerlingguy.nginx
# Install specific version
ansible-galaxy install geerlingguy.nginx,2.8.0
# Install to specific path
ansible-galaxy install geerlingguy.nginx -p ./roles
# Install from requirements file
ansible-galaxy install -r requirements.yaml
Requirements File
requirements.yaml:
---
# From Galaxy
- name: geerlingguy.nginx
version: 2.8.0
- name: geerlingguy.postgresql
version: 3.4.0
# From Git repository
- src: https://github.com/company/ansible-role-custom
version: main
name: custom
# From GitHub
- src: git+https://github.com/company/ansible-role-app.git
version: v1.0.0
name: app
Install all:
ansible-galaxy install -r requirements.yaml
Use Galaxy Role
---
- name: Configure servers
hosts: webservers
become: yes
roles:
- geerlingguy.nginx
- geerlingguy.postgresql
Publish Your Role
1. Create role on Galaxy (GitHub integration)
2. Push to GitHub:
cd roles/my-role
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/user/ansible-role-my-role.git
git push -u origin main
3. Import to Galaxy:
- Login to galaxy.ansible.com
- My Content โ Import
- Select repository
๐ฏ Advanced Role Patterns
Role with Multiple Task Files
roles/app/tasks/main.yaml:
---
- import_tasks: install.yaml
- import_tasks: configure.yaml
- import_tasks: deploy.yaml
- import_tasks: monitoring.yaml
roles/app/tasks/install.yaml:
---
- name: Install dependencies
apt:
name:
- python3
- python3-pip
state: present
# ... more install tasks
Conditional Task Inclusion
---
- name: Include OS-specific tasks
include_tasks: "{{ ansible_os_family }}.yaml"
- name: Include environment-specific tasks
include_tasks: "{{ env }}.yaml"
when: env is defined
Role Variables Priority
Priority order (highest to lowest):
- Extra vars (
-eon command line) - Task vars
- Block vars
- Role vars (
roles/x/vars/main.yaml) - Play vars
- Host facts
- Host vars
- Group vars
- Role defaults (
roles/x/defaults/main.yaml)
๐งช Testing Roles
Molecule
Install:
pip install molecule molecule-docker
Initialize:
cd roles/nginx
molecule init scenario
Test:
# Create test instance
molecule create
# Run converge (apply role)
molecule converge
# Run tests
molecule verify
# Destroy instance
molecule destroy
# Full test cycle
molecule test
Simple Test Playbook
roles/nginx/tests/test.yaml:
---
- name: Test nginx role
hosts: localhost
remote_user: root
roles:
- nginx
post_tasks:
- name: Check if Nginx is running
service:
name: nginx
state: started
check_mode: yes
register: result
failed_when: result.changed
- name: Check if Nginx responds
uri:
url: http://localhost
status_code: 200
๐ก Best Practices
1. Use Defaults
# roles/app/defaults/main.yaml
---
app_port: 8080
app_workers: 4
app_log_level: info
Users can override easily:
roles:
- role: app
vars:
app_port: 9000
2. Document Your Role
README.md:
# Nginx Role
Installs and configures Nginx web server.
## Requirements
- Ansible 2.9+
- Ubuntu 20.04+
## Role Variables
- `nginx_port`: Port to listen on (default: 80)
- `nginx_worker_processes`: Number of worker processes (default: auto)
## Dependencies
None
## Example Playbook
\`\`\`yaml
- hosts: webservers
roles:
- role: nginx
nginx_port: 8080
\`\`\`
## License
MIT
3. Keep Roles Focused
# Good: Focused roles
roles/
โโโ nginx/
โโโ postgresql/
โโโ redis/
# Bad: God role
roles/
โโโ everything/ # Does too much!
4. Use Meta Dependencies
# roles/webapp/meta/main.yaml
dependencies:
- common # Always needed
- nginx # Web server
5. Version Your Roles
Git tags:
git tag v1.0.0
git push --tags
Semantic versioning: major.minor.patch
- Major: Breaking changes
- Minor: New features (backward compatible)
- Patch: Bug fixes
๐ What's Next?
Now that you can create and use roles:
Security:
- ansible-vault - Encrypt sensitive data
Advanced Patterns:
- ansible-patterns - Production patterns
Testing:
- ansible-testing - Test your automation
๐ Resources
Official Docs:
Popular Roles:
Testing:
๐ Change Log
2026-01-30
- Created Ansible roles guide
- Explained role structure and components
- Demonstrated role creation process
- Covered variables and defaults
- Included role dependencies
- Introduced Ansible Galaxy
- Provided multi-role examples
- Added testing overview
- Included best practices
Next Article: ansible-vault - Secure your secrets!