Skip to main content
RunBook Academy

AnsibleXVI · HandlersHandlers

listen topics as a handler interface

Intermediate⏱ ~18 minansible-playbook

What you'll learn

  • Use listen to fan one notification out to several handlers
  • Explain why a topic is a stabler contract than a handler name
  • Choose between notifying a name and notifying a topic
  • Avoid the trap of templating a listen topic

Prerequisites

Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11

Not yet marked complete on this device.

notify: Reload nginx names an implementation. The task says which handler to run, which means the task knows how the service is restarted, what the handler is called, and — implicitly — that there is exactly one of it.

listen inverts that. A handler declares which topic it subscribes to, and a task notifies the topic. The task now says what needs to happen and stays silent on how many handlers implement it or what they are called.

The mechanism

Read-only / Safeone notification, two handlers
- name: Listen topics and handler chaining
hosts: local
gather_facts: false
tasks:
  - name: Notify a topic rather than a name
    ansible.builtin.debug:
      msg: t
    changed_when: true
    notify: restart web stack

handlers:
  - name: Reload the cache layer
    ansible.builtin.debug:
      msg: cache reloaded
    listen: restart web stack

  - name: Reload the web server
    ansible.builtin.debug:
      msg: web reloaded
    listen: restart web stack
Read-only / Safeexecuted on ansible-core 2.21.3
$ ansible-playbook -i inv.ini listen.yml
TASK [Notify a topic rather than a name] ***************************************
changed: [localhost]

RUNNING HANDLER [Reload the cache layer] ***************************************
ok: [localhost]

RUNNING HANDLER [Reload the web server] ****************************************
ok: [localhost]

The handler names still appear in the output — that is what identifies them for a reader of the run log. Only the notification uses the topic.

A handler may carry both a name and a listen, and can then be notified either way. It may also listen to more than one topic:

Service impact possiblea handler on two topics
handlers:
- name: Reload nginx
  ansible.builtin.systemd_service:
    name: nginx
    state: reloaded
  listen:
    - restart web stack
    - tls material changed

Why a topic is a stabler contract

The operational argument is about who owns which side of the boundary.

Consider a webserver role with a handler called Reload nginx, and a separate play or role whose tasks deploy TLS certificates and notify it. The certificate tasks now depend on a string defined inside somebody else’s role. When that role is refactored — nginx replaced with Caddy, the handler renamed to Reload the reverse proxy, the reload split into two steps — every notifier breaks.

And it breaks in the worst available way. A notify naming a handler that no longer exists aborts the run:

Read-only / Safea notify that names nothing
$ ansible-playbook -i inv.ini typo.yml
PLAY [A notify that names no handler] ******************************************

TASK [Deploy the configuration] ************************************************
[ERROR]: The requested handler 'Reload the sevrice' was not found in either the main handlers list nor in the listening handlers list

An immediate abort is at least visible. But it happens at run time on whichever host reaches that task first, potentially mid-rollout, with part of the fleet already changed.

With a topic, the role publishes restart web stack as its interface and is free to rename, split or replace the handlers behind it. The notifiers keep working because they never referred to an implementation in the first place.

The fan-out is the point, and also the risk

One notification running four handlers is convenient. It is also four things happening on the host from one line of YAML that does not say so.

Service impact possiblewhat one notify can be worth
handlers:
- name: Reload nginx
  ansible.builtin.systemd_service:
    name: nginx
    state: reloaded
  listen: tls material changed

- name: Restart the API service
  ansible.builtin.systemd_service:
    name: billing-api
    state: restarted
  listen: tls material changed

- name: Restart the message broker
  ansible.builtin.systemd_service:
    name: broker
    state: restarted
  listen: tls material changed

nginx reloads without dropping connections. billing-api and broker are full restarts. A certificate renewal that renders every 60 days has just become a three-service restart across every host that renewed — and the task that triggered it says only notify: tls material changed.

The rule that follows: the blast radius of a notify is the handlers section, not the notify line. When reviewing a change that adds a notify, read the subscribers.

Knowledge check

Knowledge check · 4 questions

  1. Q1. A role you do not own defines a handler named Reload nginx. Your certificate tasks notify it by name. The role is refactored and the handler is renamed. What happens on the next run?

  2. Q2. Two handlers subscribe to the topic restart web stack: Reload the cache layer, defined first, and Reload the web server, defined second. A task notifies the topic once. What runs?

  3. Q3. Which of these are true of listen topics? Select all that apply.

  4. Q4. Because a topic can fan out to several handlers, reading the notify line is enough to know what a change will do to the host.

Passing score: 75%. Answers are checked in this browser.