Ansible Handlers and Notify Explained with Examples – Restart Services Only When Changes Occur

Hello DevOps Experts!! let's zoom  into the usage of the Ansible Handlers and notifies 

Have you ever restarted a service in Ansible even though nothing actually changed?

Many beginners add service restart tasks directly into their playbooks. While this works, it can cause unnecessary service interruptions, increase deployment times, and introduce avoidable downtime.

Ansible provides a smarter mechanism called Handlers, which execute only when a task reports a change. Combined with the notify directive, handlers help build efficient, idempotent, and production-ready automation.

Ansible Handlers Best practices


In this article, we'll explore how Ansible Handlers work, when to use them, and common DevOps use cases.

What We'll Learn

By the end of this article, you will understand:

  • What Ansible Handlers are
  • Why handlers are important
  • How the notify keyword works
  • When handlers are triggered
  • How multiple tasks can notify a single handler
  • Real-world DevOps use cases
  • Best practices for handler design
  • Common mistakes and troubleshooting tips

What are Ansible Handlers?

The handlers section or the tasks defined under the handlers folder are executed at the end of the play once all tasks are finished. In the handlers tasks we are typically do either start, reload, restart and stop services.

Sometimes we may need to execute the task only when a particular change is made that can be notified.  Simple example of Apache web server when we modify httpd.conf file then we want to restart the httpd service. 

Handlers typically perform actions such as:

  • Restarting services
  • Reloading services
  • Restarting applications
  • Reloading firewall configurations
  • Reloading NGINX or Apache configurations
  • Restarting Tomcat after configuration updates
  • Reloading systemd daemon

Why we need Ansible Task handlers?

Let's take an use case to understand this clearly, consider Apache Web Server. Suppose we have updated the configuration file: /etc/httpd/conf/httpd.conf 

To reflect the changes we need to restart the httpd service. 

Before Handlers use :

tasks:
  - name: Update Apache configuration
    template:
      src: httpd.conf.j2
      dest: /etc/httpd/conf/httpd.conf

  - name: Restart Apache
    service:
      name: httpd
      state: restarted


Here is the problem?
Apache restarts every time the playbook runs, even if the configuration file remains unchanged.
This creates unnecessary downtime.

How the Handlers solves this problem?

With handlers logic implementation here:
tasks:
  - name: Update Apache configuration
    template:
      src: httpd.conf.j2
      dest: /etc/httpd/conf/httpd.conf
    notify:
      - Restart Apache
define the Handler:
handlers:
  - name: Restart Apache
    service:
      name: httpd
      state: restarted
Now Apache restarts only when the configuration file changes.
This is one of the core principles of idempotent automation.

How Notify Works with Handlers

The notify keyword tells Ansible Controller which handler should be triggered.
Example:
notify:
  - Restart Apache
When the task reports:
changed=1
Ansible schedules the handler execution.
If the task reports:
changed=0
Observe that - The handler execution is skipped.

So here the point is that handler tasks will be performed only when they are notified.

Ensure that handler task name should have a globally unique name.

Important Behavior of Handlers

Many new Ansible automation users expect handlers to run immediately, Even I thought the same but after reading documentation got it clear understanding.

However, handlers are executed:
  1. ✅ After all normal tasks complete
  2. ❌ Not immediately after notification
Let's check with this Example:
tasks:

  - Task A
    notify: Restart Apache

  - Task B

  - Task C
Execution order for the above snippet:
Task A
Task B
Task C
Restart Apache

This behavior prevents multiple unnecessary service restarts.

Real-World Example: Apache Web Server for PHP

Role Structure

php-webserver/
├── tasks/
│   └── main.yml
├── handlers/
│   └── main.yml
tasks/main.yml
---
- name: Install PHP packages
  yum:
    name: "{{ item }}"
    state: latest
  loop:
    - php
    - php-gd
    - php-pear
    - php-mysql

  notify:
    - Restart Apache
    
handlers/main.yml
---
- name: Restart Apache
  service:
    name: httpd
    state: restarted

 Let's see the example of notify and handlers

Calling the role

Now you can see how to include the role php-webserver into the main playbook
# Filename: test-handler.yml
---
 - hosts: web
   user: root
   become: yes
   roles:
     - php-webserver  

Save the file as test-handler.yml. Now, Execution of the test-handler will be like this:

ansible-playbook test-handler.yml

Real-World DevOps Use Cases

1. Reload system's Firewalld

- name: Add firewall rule
  firewalld:
    port: 8080/tcp
    permanent: yes
    state: enabled

  notify:
    - Reload Firewalld
Handler for firewal deamon reload:
- name: Reload Firewalld
  service:
    name: firewalld
    state: reloaded

2. Restart Tomcat when app deployed

- name: Deploy application
  copy:
    src: app.war
    dest: /opt/tomcat/webapps/

  notify:
    - Restart Tomcat
Handler for restarting Tomcat server:
- name: Restart Tomcat
  service:
    name: tomcat
    state: restarted

3. Reload NGINX when conf updates

- name: Update nginx.conf
  template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf

  notify:
    - Reload NGINX
Handler for reload NGINX web server:
- name: Reload NGINX
  service:
    name: nginx
    state: reloaded

4.Multiple Tasks Can Notify the Same Handler

This is valication of a powerful feature of Handler in Ansible.

- name: Update vhost config
  template:
    src: vhost.conf.j2
    dest: /etc/httpd/conf.d/vhost.conf
  notify:
    - Restart Apache

- name: Update httpd.conf
  template:
    src: httpd.conf.j2
    dest: /etc/httpd/conf/httpd.conf
  notify:
    - Restart Apache

Even if both tasks change, Apache restarts only once.
This improves performance and reduces service disruptions.

Force Immediate Handler Execution

Sometimes you need the handler to run before later tasks.
Use:

- meta: flush_handlers
Example:
tasks:

  - name: Update configuration
    template:
      src: app.conf.j2
      dest: /etc/app.conf
    notify:
      - Restart App

  - meta: flush_handlers

  - name: Validate application
    shell: appctl status
Here the restart happens immediately before validation.

Best practices for Ansible Handlers

1. Use Meaningful Names

Better naming samples:

Restart Apache
Reload Firewalld
Restart Tomcat
Avoid:
handler1
restart
task1

2. Use Reload Instead of Restart When Possible

Reload causes less disruption.
Examples:
NGINX
Apache
HAProxy
  

3. Keep Handlers Idempotent

Handlers should safely execute multiple times without causing unexpected side effects.

4. Centralize Service Management

Keep service restart logic inside handlers rather than scattering restart tasks throughout playbooks.

Don't do this Mistakes

Handler Name Typo
Task:

notify:
  - Restart Apache
Handler:
- name: Restart HTTPD
Result:
ERROR! The requested handler was not found
Names must match exactly. Expecting Immediate Execution Be aware of that Handlers run at the end of the play unless:
meta: flush_handlers
is used. Using Restart Instead of Reload For services supporting configuration reloads, use:
state: reloaded
instead of:
state: restarted
when appropriate and applicable.

Benefits of Using Handlers

BenefitDescription
Reduced DowntimeServices restart only when required
Better PerformanceFewer unnecessary operations
IdempotencySupports repeatable automation
Cleaner PlaybooksService management is centralized
ScalabilitySuitable for large environments
Production SafetyReduces accidental disruptions

Reader Challenge

Try implementing the following:

Beginner

  • Restart Apache only when httpd.conf changes

Intermediate

  • Reload NGINX after virtual host updates

Advanced

  • Use multiple notifications for a single handler
  • Implement flush_handlers
  • Build reusable handlers within Ansible roles

Conclusion

Ansible Handlers are one of the most important features for building efficient and production-ready automation. They ensure that services are restarted, reloaded, or refreshed only when necessary, helping reduce downtime and maintain idempotent deployments.

Whether you are managing Apache, NGINX, Tomcat, Kubernetes nodes, or enterprise applications, understanding handlers and notifications will make your Ansible automation cleaner, faster, and more reliable.

Dear Reader Question

Which service do you most commonly manage with Ansible handlers—Apache, NGINX, Tomcat, Docker, Kubernetes, or something else?

Share your experience in the comments and let fellow automation engineers learn from your real-world use cases.

Comments

Popular Articles

DevOps Weapons

Ansible URI Module Tutorial: Real-World Application Health Checks, REST API Validation and DevOps Automation

Ansible Jinja Templates Explained with Real world examples