Ansible modules are designed to be idempotent. This means that if you run a playbook multiple times, the result is always the same. You can run plays and their tasks multiple times, but managed hosts are only changed if those changes are required to get the managed hosts to the desired state.
However, sometimes when a task does make a change to the system, a further task might need to be run. For example, a change to a service's configuration file might then require that the service be reloaded so that the changed configuration takes effect.
Handlers are tasks that respond to a notification triggered by other tasks. Tasks only notify their handlers when the task changes something on a managed host. Each handler is triggered by its name after the play's block of tasks.
If no task notifies the handler by name then the handler does not run. If one or more tasks notify the handler, the handler runs once after all other tasks in the play have completed. Because handlers are tasks, administrators can use the same modules in handlers that they would use for any other task.
Normally, handlers are used to reboot hosts and restart services.
Use unique names for your handlers. When multiple handlers are defined with the same name, only the last handler defined with the shared name runs.
Handlers can be considered as inactive tasks that only get triggered when explicitly invoked using a notify statement.
The following snippet shows how the Apache server is only restarted by the restart apache handler when a configuration file is updated and notifies it:
tasks: - name: copy demo.example.conf configuration templateansible.builtin.template: src: /var/lib/templates/demo.example.conf.template dest: /etc/httpd/conf.d/demo.example.conf notify:
- restart apache
handlers:
- name: restart apache
ansible.builtin.service:
name: httpd state: restarted
The task that notifies the handler. | |
The | |
The name of the handler to run. | |
The | |
The name of the handler invoked by tasks. | |
The module to use for the handler. |
In the previous example, the restart apache handler is triggered when notified by the template task that a change happened.
A task might call more than one handler in its notify section.
Ansible treats the notify statement as an array and iterates over the handler names:
tasks:
- name: copy demo.example.conf configuration template
ansible.builtin.template:
src: /var/lib/templates/demo.example.conf.template
dest: /etc/httpd/conf.d/demo.example.conf
notify:
- restart mysql
- restart apache
handlers:
- name: restart mysql
ansible.builtin.service:
name: mariadb
state: restarted
- name: restart apache
ansible.builtin.service:
name: httpd
state: restartedAs discussed in the Ansible documentation, there are some important things to remember about using handlers:
Handlers always run in the order specified by the handlers section of the play.
They do not run in the order in which they are listed by notify statements in a task, or in the order in which tasks notify them.
Handlers normally run after all other tasks in the play complete.
A handler called by a task in the tasks part of the playbook does not run until all tasks under tasks have been processed.
(Some minor exceptions to this exist.)
Handler names exist in a per-play namespace. If two handlers are incorrectly given the same name, only one of them runs.
Even if more than one task notifies a handler, the handler runs one time. If no tasks notify it, the handler does not run.
If a task that includes a notify statement does not report a changed result (for example, a package is already installed and the task reports ok), the handler is not notified.
Ansible notifies handlers only if the task reports the changed status.
Handlers are meant to perform an extra action when a task makes a change to a managed host. They should not be used as a replacement for normal tasks.