AnsiblePilot — Ansible Automation Platform and Enterprise Ansible

AnsiblePilot covers Red Hat Ansible Automation Platform (AAP) and enterprise Ansible: install and upgrade paths, Event-Driven Ansible, security and day-2 operations, plus Luca Berton's books, videos and training.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 9 technical books published by Apress and Leanpub, 4 of them on Ansible, including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Multiple Handlers: Run Handlers in Sequence (listen, notify)

By Luca Berton · Published 2023-03-13 · Category: installation

How to run multiple Ansible handlers from a single task. Use listen groups and multiple notify entries to chain handler execution with practical examples.

Ansible Multiple Handlers: Run Handlers in Sequence (listen, notify)

Two ways to run multiple Ansible handlers

How to execute two Ansible handlers on a changed status of Ansible Playbook.

See also: Ansible Handlers Explained: How to Use and Flush Handlers

What is an Ansible handler?

> Handler runs tasks on change.

Handlers execute some tasks only when the previous task returns a changed status. If not necessary, they don't execute.

  • https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_handlers.html

Demo

Let's jump into two real-life examples of how to run multiple Ansible handlers. First of all, we need a task changed status. The simplest Ansible module returning a "changed" status is the command module with a Linux command, like "uptime". Let's suppose we would like to execute two handlers on the screen, for example, two messages on the screen.

See also: Ansible Scheduled Tasks: Cron Jobs & at Module for Timed Execution

Solution 1

code

  • two-1.yml
---
- name: handler Playbook
  hosts: all
  tasks:
    - name: Test connection
      ansible.builtin.command: "uptime"
      notify: message

  handlers:
    - name: message 1
      ansible.builtin.debug:
        msg: message 1
      listen: message

    - name: message 2
      ansible.builtin.debug:
        msg: message 2
      listen: message
  • inventory
localhost ansible_connection=local

execution

$ ansible-playbook -i inventory two-1.yml

PLAY [handler Playbook] *********************************************************************

TASK [Gathering Facts] ******************************************************************
ok: [localhost]

TASK [Test connection] ******************************************************************
changed: [localhost]

RUNNING HANDLER [message 1] *************************************************************
ok: [localhost] => {
    "msg": "message 1"
}

RUNNING HANDLER [message 2] *************************************************************
ok: [localhost] => {
    "msg": "message 2"
}

PLAY RECAP ******************************************************************************
localhost                  : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

See also: Securely Automate Sudo Passwords in Ansible Playbooks

Solution 2

code

  • two-2.yml
---
- name: handler Playbook
  hosts: all
  tasks:
    - name: Test connection
      ansible.builtin.command: "uptime"
      notify:
        - message 1
        - message 2

  handlers:
    - name: message 1
      ansible.builtin.debug:
        msg: message 1

    - name: message 2
      ansible.builtin.debug:
        msg: message 2

execution

$ ansible-playbook -i inventory two-1.yml

PLAY [handler Playbook] *********************************************************************

TASK [Gathering Facts] ******************************************************************
ok: [localhost]

TASK [Test connection] ******************************************************************
changed: [localhost]

RUNNING HANDLER [message 1] *************************************************************
ok: [localhost] => {
    "msg": "message 1"
}

RUNNING HANDLER [message 2] *************************************************************
ok: [localhost] => {
    "msg": "message 2"
}

PLAY RECAP ******************************************************************************
localhost                  : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
PLAY [handler Playbook] *********************************************************************

Conclusion

Now you know Two ways to run multiple Ansible handlers.

Method 1: Multiple notify entries

tasks:
  - name: Update nginx config
    ansible.builtin.template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify:
      - Validate nginx config
      - Restart nginx

handlers:
  - name: Validate nginx config
    ansible.builtin.command: nginx -t
    changed_when: false

  - name: Restart nginx
    ansible.builtin.service:
      name: nginx
      state: restarted
tasks:
  - name: Update nginx config
    ansible.builtin.template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify: restart web stack

handlers:
  - name: Validate config
    ansible.builtin.command: nginx -t
    listen: restart web stack

  - name: Restart nginx
    ansible.builtin.service:
      name: nginx
      state: restarted
    listen: restart web stack

  - name: Clear cache
    ansible.builtin.file:
      path: /var/cache/nginx
      state: absent
    listen: restart web stack

Handler Rules

RuleDescription
Run onceEven if notified multiple times
End of playRun after all tasks, not after each
OrderRun in order defined, not notified
ConditionalOnly if notified (something changed)

Force handlers mid-play

- name: Update config
  template:
    src: config.j2
    dest: /etc/myapp/config.yml
  notify: restart myapp

- meta: flush_handlers  # Runs handlers NOW

- name: Verify service
  uri:
    url: http://localhost:8080/health

Chained Handlers

handlers:
  - name: Validate config
    command: nginx -t
    notify: Reload nginx  # Only reloads if validation passes

  - name: Reload nginx
    service:
      name: nginx
      state: reloaded

Deployment Pattern

tasks:
  - name: Deploy code
    ansible.builtin.git:
      repo: https://github.com/myorg/myapp.git
      dest: /opt/myapp
    notify: deploy application

handlers:
  - name: Install deps
    command: pip install -r requirements.txt
    args:
      chdir: /opt/myapp
    listen: deploy application

  - name: Run migrations
    command: python manage.py migrate
    args:
      chdir: /opt/myapp
    listen: deploy application

  - name: Restart app
    systemd:
      name: myapp
      state: restarted
    listen: deploy application

FAQ

Why didn't my handler run?

Handlers only run when a task reports changed. If the task shows ok, the handler is not notified.

Can handlers from different roles listen to the same topic?

Yes - that's a key advantage of listen over direct notify.

Category: installation

Watch the video: Ansible Multiple Handlers: Run Handlers in Sequence (listen, notify) — Video Tutorial

Browse all Ansible tutorials · AnsiblePilot Home