
Un handler est une tâche réactive qui ne s'exécute que si une autre tâche l'a notifiée. C'est le mécanisme central du pattern restart-on-config-change : vous modifiez /etc/nginx/nginx.conf, et nginx est rechargé uniquement si la configuration a vraiment changé. Sans handlers, vous redémarreriez nginx à chaque exécution du playbook, ce qui casse l'idempotence et provoque des coupures de service inutiles.
Cette page couvre tout ce qu'il faut savoir : déclenchement via notify:, regroupement via listen:, exécution forcée avec meta: flush_handlers, ordre d'exécution exact dans un play, et les pièges classiques (handler qui ne se déclenche pas, double notification, ordre inversé).
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Le concept d'un handler comme tâche réactive (pas exécutée par défaut) ;
- Le déclenchement via
notify:(un seul ou plusieurs handlers) ; - Le regroupement via
listen:(plusieurs handlers réagissent à un même topic) ; - L'ordre d'exécution : à la fin de chaque section (
pre_tasks,tasks,post_tasks) ; meta: flush_handlerspour forcer un déclenchement immédiat ;- Le pattern restart-on-config-change appliqué à nginx, sshd, postgresql.
Le pattern restart-on-config-change
Section intitulée « Le pattern restart-on-config-change »Cas typique : vous gérez /etc/nginx/nginx.conf et voulez que nginx recharge sa config uniquement si le fichier a changé. Sans handler :
# ❌ Anti-pattern : restart à chaque run, même si rien n'a changé- name: Mettre à jour nginx.conf ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf
- name: Redémarrer nginx ansible.builtin.systemd: name: nginx state: restarted # ← exécuté CHAQUE FOISAvec un handler, le redémarrage n'a lieu que si le fichier change :
# ✅ Pattern restart-on-config-change avec handlertasks: - name: Mettre à jour nginx.conf ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: Recharger nginx # ← notifie le handler
handlers: - name: Recharger nginx ansible.builtin.systemd: name: nginx state: reloadedAu second run, si le fichier n'a pas changé, le handler n'est pas déclenché et changed=0. C'est l'idempotence appliquée aux services.
Déclenchement avec notify:
Section intitulée « Déclenchement avec notify: »Une tâche notifie un handler par son name: exact :
- name: Mettre à jour la config ansible.builtin.copy: src: app.conf dest: /etc/app/app.conf mode: "0644" notify: Restart app # nom EXACT du handlerPlusieurs handlers possibles :
- name: Mettre à jour la config ansible.builtin.copy: src: app.conf dest: /etc/app/app.conf mode: "0644" notify: - Restart app - Notifier Slack - Vider le cacheRègle d'or : un handler n'est notifié que si la tâche est en changed. Si la tâche est ok (rien à faire), le handler ne se déclenche pas. C'est exactement ce qu'on veut.
Regroupement avec listen:
Section intitulée « Regroupement avec listen: »listen: permet de découpler la notification du nom du handler. Plusieurs handlers peuvent écouter le même topic :
tasks: - name: Mettre à jour la config nginx ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: "config nginx changée"
- name: Mettre à jour le vhost ansible.builtin.template: src: site.conf.j2 dest: /etc/nginx/conf.d/site.conf notify: "config nginx changée"
handlers: - name: Recharger nginx ansible.builtin.systemd: name: nginx state: reloaded listen: "config nginx changée"
- name: Recharger fail2ban (qui dépend de nginx) ansible.builtin.systemd: name: fail2ban state: reloaded listen: "config nginx changée"Avec listen:, vous notifiez un événement abstrait ; tous les handlers qui écoutent ce topic se déclenchent. Pratique pour les rôles publics (Galaxy) qui ne veulent pas figer le nom des handlers.
L'ordre d'exécution
Section intitulée « L'ordre d'exécution »Les handlers s'exécutent à la fin de chaque section qui les a notifiés :
1. gather_facts2. pre_tasks → handlers notifiés ici tournent maintenant3. roles → handlers notifiés ici tournent maintenant4. tasks → handlers notifiés ici tournent maintenant5. post_tasks → handlers notifiés ici tournent maintenant6. fin du playConséquence : si une tasks: modifie nginx.conf et notifie Recharger nginx, le handler tourne avant post_tasks:. Donc dans post_tasks, vous pouvez tester http://localhost et nginx aura déjà sa nouvelle config.
Ordre interne : les handlers s'exécutent dans l'ordre de leur déclaration dans handlers:, pas dans l'ordre des notify:. Si vous voulez un ordre précis, déclarez les handlers dans cet ordre.
meta: flush_handlers, forcer l'exécution
Section intitulée « meta: flush_handlers, forcer l'exécution »Parfois vous avez besoin que le handler tourne immédiatement, sans attendre la fin de la section. Le module meta avec flush_handlers force l'exécution :
tasks: - name: Mettre à jour la config ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: Recharger nginx
- name: Forcer le reload immédiat ansible.builtin.meta: flush_handlers
- name: Tester que la nouvelle config répond ansible.builtin.uri: url: http://localhost status_code: 200Sans flush_handlers:, le uri: testerait l'ancienne config (handler pas encore déclenché). Avec, nginx est rechargé entre les deux tâches.
Idempotence du déclenchement
Section intitulée « Idempotence du déclenchement »Un handler est dédupliqué : si plusieurs tâches notifient Recharger nginx, le handler tourne une seule fois à la fin. Aucun risque de redémarrer 5 fois nginx parce que 5 tâches ont modifié sa config.
Cette propriété est ce qui rend le pattern restart-on-config-change propre et performant.
Cas concret, sshd avec validation
Section intitulée « Cas concret, sshd avec validation »Pour les services critiques comme sshd, il faut valider la nouvelle config avant d'appliquer le restart. Sinon vous risquez de vous bloquer hors du serveur :
tasks: - name: Mettre à jour sshd_config ansible.builtin.template: src: sshd_config.j2 dest: /etc/ssh/sshd_config validate: '/usr/sbin/sshd -t -f %s' # ← validation préalable backup: true notify: Restart sshd
handlers: - name: Restart sshd ansible.builtin.systemd: name: sshd state: restartedvalidate: '/usr/sbin/sshd -t -f %s' lance sshd -t -f /tmp/<temp>.conf avec le fichier généré. Si la commande échoue (config invalide), le template: ne remplace pas le fichier final et le handler n'est pas notifié. Votre serveur reste accessible.
Pièges fréquents
Section intitulée « Pièges fréquents »| Symptôme | Cause | Fix |
|---|---|---|
| Handler jamais déclenché | Le notify: ne match pas exactement le name: du handler | Vérifier l'orthographe (case-sensitive, espaces) |
| Handler exécuté plusieurs fois | Faux : Ansible déduplique. Si vous le voyez plusieurs fois, c'est qu'il y a plusieurs handlers du même nom | Renommer les handlers ou utiliser listen: |
| Handler exécuté trop tard | Vous attendiez qu'il tourne avant la tâche suivante | Ajouter - meta: flush_handlers après la notif |
notify: sur une tâche ok=true | Normal : un handler ne se déclenche que sur changed | Forcer avec changed_when: true (rarement justifié) |
notify: ignore une variable | notify: ne supporte pas Jinja2 dans le nom au runtime | Utiliser listen: qui accepte des topics dynamiques |
| Service qui ne redémarre pas | Le service est state: reloaded mais le binaire a changé | Utiliser state: restarted (reload seul ne recharge pas le binaire) |
À retenir
Section intitulée « À retenir »- Un handler est une tâche réactive déclenchée par
notify:, uniquement si la tâche notificatrice estchanged. listen:regroupe plusieurs handlers sur un topic abstrait, utile pour les rôles publics.- Les handlers s'exécutent à la fin de chaque section (
pre_tasks,tasks,post_tasks), dans l'ordre de leur déclaration. meta: flush_handlersforce l'exécution immédiate (utile avant un test post-config).- Pour les services critiques (sshd, nginx, postgresql), toujours poser un
validate:sur le moduletemplate:pour ne pas appliquer une config cassée. - Un handler est dédupliqué : 5 notifs = 1 exécution. C'est la base de l'idempotence.
Mettre en pratique
Section intitulée « Mettre en pratique »Un handler ne se déclenche que si la tâche notificatrice change vraiment, condition qu'un extrait de code ne peut pas démontrer. Le lab vous fait notifier plusieurs handlers depuis une seule tâche, les découpler avec listen, forcer leur exécution immédiate par meta: flush_handlers, et poser un validate pour ne jamais appliquer une configuration invalide. Le contrôle rejoue le playbook : au second passage, aucun handler ne doit repartir, c'est la mesure de l'idempotence.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Import vs Include : le comportement des handlers change selon que vous importez ou incluez un fichier de tâches.
- Parallélisme et stratégies : ce que devient un handler pendant un rolling update piloté par
serial:. - Check mode et diff : repérer en dry-run les tâches qui passeraient en
changed, donc les handlers qui partiraient.