Ansible: Conditionally Restart Services Only If Currently Running (Cross-Platform Solution)


2 views

When automating system administration with Ansible, a common requirement is to restart services after configuration changes - but only when those services were already running. This avoids accidentally starting services that were intentionally stopped, while ensuring running services get their necessary reloads.

The standard service module's restarted state will restart regardless of initial state. While we could use shell with platform-specific commands like systemctl is-active, this breaks cross-platform compatibility - exactly what Ansible aims to solve.

Here's the cleanest approach combining Ansible's fact gathering with conditional execution:


- name: Check if service is currently running
  ansible.builtin.service:
    name: nginx
    enabled: yes
  register: service_status
  changed_when: false

- name: Restart service if it was running
  ansible.builtin.service:
    name: nginx
    state: restarted
  when: service_status.state == "running"

For better integration with configuration file changes, use handlers with the same conditional logic:


handlers:
  - name: Restart nginx if running
    ansible.builtin.service:
      name: nginx
      state: restarted
    when: service_status.state == "running"

tasks:
  - name: Check nginx status
    ansible.builtin.service:
      name: nginx
    register: service_status
    changed_when: false

  - name: Update nginx config
    ansible.builtin.template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify: Restart nginx if running

While this approach works across init systems, consider these edge cases:

  • For Docker containers: Check service_status.state against both "running" and "active"
  • For older SysV systems: Verify your Ansible version handles status commands properly

When dealing with multiple services, gather all statuses in a single task using loop:


- name: Check multiple services
  ansible.builtin.service:
    name: "{{ item }}"
  register: all_services
  changed_when: false
  loop:
    - nginx
    - postgresql
    - redis

- name: Restart all running services
  ansible.builtin.service:
    name: "{{ item.item }}"
    state: restarted
  when: item.state == "running"
  loop: "{{ all_services.results }}"

When managing services through Ansible, we often face a common scenario: needing to restart a service after configuration changes, but only if the service was already running. This becomes particularly important in production environments where we want to avoid accidentally starting services that were intentionally stopped.

Ansible's service module provides excellent cross-platform compatibility (supporting systemd, upstart, sysvinit, etc.), but lacks a built-in way to check the current state before performing actions. The typical approach using changed_when and handlers might restart services regardless of their initial state.

Here's the most elegant solution I've found that maintains portability across init systems:

- name: Gather service facts
  ansible.builtin.service_facts:
  
- name: Restart service if it was running
  ansible.builtin.service:
    name: nginx
    state: restarted
  when: ansible_facts.services['nginx.service'].state == 'running'

For more robust handling across different scenarios:

- name: Conditional service restart with fallback
  block:
    - name: Check service status
      ansible.builtin.command: systemctl is-active nginx
      register: service_status
      changed_when: false
      ignore_errors: true
    
    - name: Restart if was active
      ansible.builtin.service:
        name: nginx
        state: restarted
      when: service_status.rc == 0
  rescue:
    - name: Alternative service fact collection
      ansible.builtin.service_facts:
      
    - name: Fallback restart check
      ansible.builtin.service:
        name: nginx
        state: restarted
      when: ansible_facts.services.get('nginx.service', {}).get('state') == 'running'

For cleaner playbooks, implement this as a handler:

handlers:
  - name: Conditional restart handler
    ansible.builtin.service:
      name: "{{ service_name }}"
      state: restarted
    when: ansible_facts.services["{{ service_name }}.service"].state == 'running'

Remember that service_facts gathers information about all services. For large environments, consider:

  • Running service facts once per playbook
  • Caching facts with fact_caching
  • Using specific status commands for critical services