
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 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 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.
Pratiquer dans le lab
Section intitulée « Pratiquer dans le lab »Cette page a un lab d’accompagnement : labs/ecrire-code/handlers/ dans
stephrobert/ansible-training. Il contient
un README.md guidé, un Makefile (make verify lance les tests), et un
challenge final auto-évalué : déclencher deux handlers depuis une seule tâche avec validation httpd ServerTokens.
Une fois le lab provisionné :
cd ~/Projets/ansible-training/labs/ecrire-code/handlers/
cat README.md # tuto pas à pascat challenge/README.md # consigne du challenge finalpytest -v challenge/tests/ # lancer les tests testinfraSi les tests passent, vous maîtrisez les concepts couverts dans ce guide. En
cas de blocage, docs/troubleshooting.md
à la racine du repo couvre les pièges fréquents (rate-limit SSH, clé absente,
collection manquante).