In Ansible 2.0+, handlers are special tasks that only run when notified by other tasks. A critical aspect often misunderstood is their execution timing relative to roles and playbooks. Handlers execute at specific trigger points - not immediately when notified, but at predefined intervals in the workflow.
Handlers defined within roles follow these execution patterns:
# Example role structure
roles/
role1/
handlers/
main.yml # These handlers execute at role completion
tasks/
main.yml
role2/
handlers/
main.yml # Separate handler execution point
Key observations from Ansible's execution model:
- Handlers trigger after all tasks in their containing role complete
- They don't wait for the entire playbook to finish
- Each role's handlers execute independently
For your scenario requiring role4 handlers to complete before role6:
# playbook.yml
- hosts: all
roles:
- role1
- role2
- role3
- role4 # Contains handlers needed by role6
- { role: role6, vars: { wait_for_handlers: true } } # Custom flag
Three reliable patterns for handler dependency management:
- Handler Chaining:
# In role4/handlers/main.yml - name: Finalize role4 setup debug: msg="Role4 handlers complete" notify: role6_start_handler
- Explicit Task Dependencies:
# In role6/tasks/main.yml - meta: flush_handlers - include_tasks: main_work.yml
- Handler Forwarding:
# In playbook.yml handlers: - name: global_role4_complete include_role: name: role4 handlers_from: handlers/main.yml
Debug handler execution order:
ansible-playbook playbook.yml \
--tags=role4,role6 \
--verbose \
--extra-vars "debug_handlers=true"
In Ansible, handlers are special tasks that only execute when notified by other tasks. The key behavior that often causes confusion is that handlers run:
- At the end of each play by default (not at the end of individual roles)
- Only if they've been notified during that play's execution
When you have a playbook structure like:
- hosts: webservers
roles:
- role1
- role2
- role3
- critical_role
- dependent_role
All handlers from critical_role
will execute before dependent_role
begins, because:
- Handlers queue up during role execution
- Ansible executes all pending handlers at play completion
- Roles execute sequentially within a play
If you need handlers to run immediately after a specific task (before the next role):
- name: Force handlers to run now
meta: flush_handlers
Example usage between roles:
- hosts: webservers
roles:
- role1
- role2
- role3
tasks:
- include_role:
name: critical_role
- meta: flush_handlers
- include_role:
name: dependent_role
For complex workflows, consider:
- Explicit task dependencies:
- Separate plays:
- name: Wait for service to be ready
wait_for:
port: 8080
delay: 10
timeout: 300
- hosts: webservers
roles:
- critical_role
- hosts: webservers
roles:
- dependent_role
To verify handler timing:
ansible-playbook playbook.yml -vvv | grep -E "handler|RUNNING HANDLER"
Or add debug tasks:
handlers:
- name: restart service
debug:
msg: "Handler executed at {{ ansible_date_time.time }}"
service:
name: nginx
state: restarted