AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Handlers: Trigger Service Restarts on Change (Guide)

By Luca Berton · Published 2024-01-01 · Category: installation

Complete guide to Ansible handlers. Trigger service restarts, flush handlers, use listen, and manage handler execution order with practical examples.

Ansible handlers are a powerful feature that enhance efficiency and control in automation workflows. They allow tasks to trigger actions only when necessary, reducing redundancy and improving performance. This article explores what handlers are, how they work, and their best practices.

What Are Ansible Handlers?

Ansible handlers are special tasks that are executed only when notified by other tasks. They are typically used to perform actions like restarting services or reloading configurations after a change has been made.

Key Features:

  • Triggered Execution: Run only when explicitly notified.
  • Task Efficiency: Avoid redundant actions, such as unnecessary service restarts.
  • Reuse: Define handlers once and use them across multiple tasks.

How Do Handlers Work?

Handlers are defined just like tasks but are listed under a dedicated handlers section in a playbook. A task notifies a handler to run when it changes something.

Example: Restarting a Service

- name: Install and configure Apache
  hosts: webservers
  tasks:
    - name: Install Apache
      apt:
        name: apache2
        state: present
      notify: Restart Apache

    - name: Configure Apache
      template:
        src: apache.conf.j2
        dest: /etc/apache2/apache2.conf
      notify: Restart Apache

  handlers:
    - name: Restart Apache
      service:
        name: apache2
        state: restarted

How It Works:

  1. Tasks notify the handler (Restart Apache) only if they make changes.
  2. At the end of the play, Ansible runs the handler once, regardless of how many times it was notified.

Key Concepts of Handlers

  1. Idempotency:
Handlers, like tasks, are idempotent, ensuring consistent results.
  1. Execution Order:
Handlers are executed after all tasks in the play are completed.
  1. Notification Frequency:
A handler is triggered only once per play, even if multiple tasks notify it.
  1. Conditional Handlers:
You can use when statements to control handler execution:
   handlers:
     - name: Restart Apache
       service:
         name: apache2
         state: restarted
       when: apache_needs_restart == true
   

See also: VMware Tag Verification with Ansible

Benefits of Using Handlers

  • Performance: Avoid redundant executions by running handlers only when needed.
  • Modularity: Simplify playbooks by separating actions from tasks.
  • Consistency: Ensure actions are triggered only under specific conditions.

Common Use Cases for Handlers

  1. Service Management:
Restart or reload services after configuration changes.
  1. Database Management:
Reload databases after schema updates.
  1. Application Deployment:
Trigger actions like clearing caches or reloading environments.

Best Practices for Handlers

  1. Use Meaningful Names:
Clearly describe the action the handler performs, such as Restart Apache.
  1. Group Notifications:
Consolidate tasks that notify the same handler to avoid multiple triggers.
  1. Minimize Side Effects:
Ensure handlers perform only the necessary actions to maintain idempotency.
  1. Test Handler Logic:
Validate handlers in a test environment to ensure proper execution.

See also: Can Ansible Be Used to Manage Windows Systems?

Conclusion

Ansible handlers are an essential tool for managing dynamic, event-driven actions in playbooks. By leveraging handlers, you can enhance automation efficiency, reduce redundancy, and maintain clean, modular playbooks.

Learn More About Ansible Handlers

How Handlers Work

Handlers are tasks that only run when notified by another task that reports a change:

tasks:
  - name: Update config
    ansible.builtin.template:
      src: app.conf.j2
      dest: /etc/myapp/app.conf
    notify: restart myapp  # Only notifies if file changed

handlers:
  - name: restart myapp
    ansible.builtin.service:
      name: myapp
      state: restarted

See also: Can Ansible Manage Windows? Complete Windows Automation Guide

Handler Rules

RuleDescription
Run onceEven if notified multiple times
Run at endAfter all tasks complete (not immediately)
Run in orderOrder defined in handlers section
Skip if no changeOnly triggered by changed status

Multiple Handlers

tasks:
  - name: Update nginx config
    template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify:
      - validate nginx
      - reload nginx

handlers:
  - name: validate nginx
    command: nginx -t
    changed_when: false

  - name: reload nginx
    service:
      name: nginx
      state: reloaded

Listen Groups

tasks:
  - name: Deploy application
    git:
      repo: https://github.com/myorg/app.git
      dest: /opt/app
    notify: deploy complete

handlers:
  - name: Install dependencies
    command: pip install -r /opt/app/requirements.txt
    listen: deploy complete

  - name: Run migrations
    command: python /opt/app/manage.py migrate
    listen: deploy complete

  - name: Restart application
    service:
      name: myapp
      state: restarted
    listen: deploy complete

Flush Handlers (Run Immediately)

tasks:
  - name: Update config
    template:
      src: config.j2
      dest: /etc/myapp/config.yml
    notify: restart myapp

  - meta: flush_handlers  # Restart happens NOW

  - name: Verify service is running
    uri:
      url: http://localhost:8080/health
    retries: 5
    delay: 3

Handlers in Roles

# roles/webserver/tasks/main.yml
- name: Deploy site config
  template:
    src: site.conf.j2
    dest: /etc/nginx/sites-enabled/mysite.conf
  notify: reload nginx

# roles/webserver/handlers/main.yml
- name: reload nginx
  service:
    name: nginx
    state: reloaded

Handlers vs Regular Tasks

FeatureHandlerTask
When runsOnly on change notificationAlways (unless when:)
Run countOnce per play (deduplicated)Every time
TimingEnd of play (or flush_handlers)In sequence
Triggered bynotify keywordPlay execution order

FAQ

Why didn't my handler run?

  1. The notifying task reported ok (no change), not changed
  2. The handler name doesn't match the notify string exactly
  3. The play failed before handlers ran (use --force-handlers to override)

Can I force handlers to always run?

ansible-playbook site.yml --force-handlers

This runs notified handlers even if later tasks fail.

How do I notify a handler from a role?

Use the handler's full name: notify: "role_name : handler_name" or use listen groups for cross-role notifications.

Basic Handler

- hosts: webservers
  tasks:
    - name: Update nginx config
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: restart nginx
      become: true

  handlers:
    - name: restart nginx
      ansible.builtin.service:
        name: nginx
        state: restarted
      become: true

Handlers run once at the end of the play, only if notified.

Multiple Notifications

tasks:
  - template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify:
      - restart nginx
      - reload firewall
    become: true

handlers:
  - name: restart nginx
    service: { name: nginx, state: restarted }
    become: true

  - name: reload firewall
    service: { name: firewalld, state: reloaded }
    become: true

Listen (Group Notifications)

tasks:
  - template:
      src: ssl-cert.j2
      dest: /etc/ssl/app.crt
    notify: ssl changed

handlers:
  - name: restart nginx
    service: { name: nginx, state: restarted }
    listen: ssl changed
    become: true

  - name: restart haproxy
    service: { name: haproxy, state: restarted }
    listen: ssl changed
    become: true

Flush Handlers (Run Immediately)

tasks:
  - template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify: restart nginx

  # Force handlers to run NOW (before next task)
  - meta: flush_handlers

  - uri:
      url: http://localhost/health
      status_code: 200

Handler Execution Order

# Handlers run in DEFINITION order, not notification order
handlers:
  - name: restart nginx    # Runs first (defined first)
    service: { name: nginx, state: restarted }

  - name: clear cache      # Runs second
    command: /opt/clear-cache.sh

  - name: send notification  # Runs third
    uri:
      url: https://hooks.slack.com/...
      method: POST
      body: '{"text": "Deploy complete"}'

Handler in Roles

# roles/webserver/handlers/main.yml
- name: restart nginx
  service:
    name: nginx
    state: restarted
  become: true

- name: reload nginx
  service:
    name: nginx
    state: reloaded
  become: true

# roles/webserver/tasks/main.yml
- template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: restart nginx  # References handler in same role

Conditional Handlers

handlers:
  - name: restart nginx
    service:
      name: nginx
      state: restarted
    when: ansible_os_family == "Debian"
    become: true

Handler with Variables

handlers:
  - name: restart app
    service:
      name: "{{ app_service_name }}"
      state: restarted
    become: true

Common Patterns

# Reload vs restart
handlers:
  - name: reload nginx    # Graceful — no downtime
    service: { name: nginx, state: reloaded }

  - name: restart nginx   # Full restart — brief downtime
    service: { name: nginx, state: restarted }

# Chain handlers
tasks:
  - template: { src: app.conf.j2, dest: /etc/app.conf }
    notify: validate and restart

handlers:
  - name: validate and restart
    command: /opt/app/validate-config.sh
    notify: restart app

  - name: restart app
    service: { name: myapp, state: restarted }

Handler Pitfalls

# Handler NOT triggered if task is "ok" (no change)
- copy:
    src: config.conf
    dest: /etc/app/config.conf
  notify: restart app
# If file already matches → no change → handler NOT called

# Handler runs only ONCE even if notified multiple times
- template: { src: a.conf.j2, dest: /etc/a.conf }
  notify: restart app
- template: { src: b.conf.j2, dest: /etc/b.conf }
  notify: restart app
# restart app runs once, not twice

FAQ

When do handlers run?

At the end of each play (after all tasks), or when explicitly flushed with meta: flush_handlers.

Do handlers run if a task fails?

No — by default, handlers don't run if any task fails. Use --force-handlers or force_handlers: true to override.

Can I call a handler from another handler?

Yes — use notify in the handler definition. They chain in definition order.

Handler vs post_tasks?

Handlers are conditional (only run on change). post_tasks always run after roles and tasks.

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home