What are handlers and notify, and why do they beat a plain "restart Apache" task?
A handler is a task that runs only when another task notifies it and that task actually reported changed. It runs once, at the end of the play, however many tasks notified it.
tasks:
- name: apache2 listen on port 8081
lineinfile:
dest: /etc/apache2/ports.conf
regexp: "^Listen 80"
line: "Listen 8081"
notify:
- restart apache2
- name: apache2 virtualhost on port 8081
lineinfile:
dest: /etc/apache2/sites-available/000-default.conf
regexp: '^<VirtualHost \*:80>'
line: "<VirtualHost *:8081>"
notify:
- restart apache2
handlers:
- name: restart apache2
service:
name: apache2
state: restarted
* Several notifications, one restart, and only if something really changed. *
A plain service: state=restarted task restarts Apache on every run, interrupting users even when nothing changed, and it breaks the "second run reports no changes" test. Handlers fix both:
- Conditional: the handler fires only if a notifying task reported
changed. Rerun the playbook with nothing to do, and Apache isn't touched. - Deduplicated: three config tasks notifying the same handler cause one restart, not three.
- Deferred: handlers run after all tasks of the play, so the restart happens once the whole configuration is in place.
The notify value must match the handler's name exactly.
A detail in the second task: regexp is a regular expression, where * means "repeat the previous character". Written unescaped as ^<VirtualHost *:80>, it would look for <VirtualHost followed by any number of spaces and :80>, which never matches the real line, so lineinfile would append a second VirtualHost line instead of replacing it. Escaping it as \* (inside single quotes, so YAML leaves the backslash alone) matches the literal asterisk.
Go deeper:
Handlers: running operations on change (official docs) โ notify, run-once semantics,
meta: flush_handlers.