Ansible modules are designed to be idempotent. This means that in a properly written playbook, the playbook and its tasks can be run multiple times without changing the managed host unless they need to make a change to get the managed host to the desired state. However, sometimes when a task does make a change to the system, a further task may need to be run. For example, a change to a service’s configuration file may 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 has a globally unique name and is triggered at the end of a block of tasks in a playbook. If no task notifies the handler by name then the handler will not run. If one or more tasks notify the handler, the handler will run exactly 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.
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:
1. The task that notifies the handler.
2. The notify statement indicates the task needs to trigger a handler. The name of the handler to run.
3. The handlers keyword indicates the start of the list of handler tasks.
4. The name of the handler invoked by tasks.
5. The module to use for the handler.
In the previous example, the restart apache handler triggers when notified by the template task that a change happened. A task may 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 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 service: name: mariadb state: restarted - name: restart apache service: name: httpd state: restarted
Benefits of Using Handlers
As 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 will not run until all tasks under tasks have been processed. (There are some minor exceptions to this.)
- Handler names exist in a per-play namespace. If two handlers are incorrectly given the same name, only one will run.
- Even if more than one task notifies a handler, the handler only runs once. If no tasks notify it, a handler will 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. The handler is skipped unless another task notifies it. Ansible notifies handlers only if the task reports the changed status.