Ansible Stop & Disable Service: systemd Module Guide (with Examples)
By Luca Berton · Published 2024-01-01 · Category: installation
How to stop and disable services on boot with Ansible systemd and service modules. Manage systemd units, check service_facts, ensure services stay stopped.

How to stop and disable services on boot on Linux remote hosts with Ansible?
Use ansible.builtin.service with state: stopped and enabled: false to stop a service immediately and prevent it from starting at boot. Run ansible.builtin.service_facts first to confirm the service exists before acting — this avoids task failures when the service may not be present on all targets.
See also: Ansible Start & Enable Service on Boot: systemd Module Guide
Ansible enable services on boot on remote hosts
- ansible.builtin.service_facts
- Return service state information as fact data
- ansible.builtin.service
- Manage services
service_facts and service.
First, you need to acquire the information of the services on the target machine.
This task is performed by the Ansible module service_facts. You can't enable a service that doesn't exist, can you?
The effective actions are performed by the Ansible module service.
The full name is ansible.builtin.service which means that both these modules are part of the collection of modules "builtin" with Ansible and shipped with it.
This module is pretty stable and out for years and its purpose is to manage services on remote hosts.
For Windows targets, use the ansible.windows.win_service module instead.
Parameters
- name path - name of the service
- state string - started / stopped / restarted / reloaded
- enabled boolean - no/yes
- arguments/args string - extra args
## Playbook
Stop and disable services on boot on Linux remote hosts with Ansible Playbook. Included code and Playbook with chronyd.service NTP server on a RedHat Enterprise Linux 8.
code
- service_stop_disable_on_boot.yml
---
- name: service module Playbook
hosts: all
become: true
vars:
disable_services:
- "chronyd.service"
tasks:
- name: populate service facts
ansible.builtin.service_facts:
- name: disable services
ansible.builtin.service:
name: "{{ item }}"
enabled: false
state: stopped
when: "item in services"
with_items: '{{ disable_services }}'
execution
$ ansible-playbook -i virtualmachines/demo/inventory services/service_stop_disable.yml
PLAY [service module Playbook] ************************************************************************
TASK [Gathering Facts] ****************************************************************************
ok: [demo.example.com]
TASK [populate service facts] *********************************************************************
ok: [demo.example.com]
TASK [disable services] ***************************************************************************
changed: [demo.example.com] => (item=chronyd.service)
PLAY RECAP ****************************************************************************************
demo.example.com : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
before execution
$ ssh devops@demo.example.com
[devops@demo ~]$ sudo su
[root@demo devops]# cat /etc/redhat-release
Red Hat Enterprise Linux release 8.4 (Ootpa)
[root@demo devops]# rpm -qa | grep chrony
chrony-3.5-2.el8.x86_64
[root@demo devops]# systemctl status chronyd.service
● chronyd.service - NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2021-11-29 11:31:32 UTC; 1 day 21h ago
Docs: man:chronyd(8)
man:chrony.conf(5)
Process: 825 ExecStartPost=/usr/libexec/chrony-helper update-daemon (code=exited, status=0/SUCCE>
Process: 811 ExecStart=/usr/sbin/chronyd $OPTIONS (code=exited, status=0/SUCCESS)
Main PID: 820 (chronyd)
Tasks: 1 (limit: 4943)
Memory: 1.3M
CGroup: /system.slice/chronyd.service
└─820 /usr/sbin/chronyd
Nov 30 13:59:47 demo.example.com chronyd[820]: Can\'t synchronise: no majority
Nov 30 14:20:47 demo.example.com chronyd[820]: Selected source 147.251.48.140
Nov 30 14:33:43 demo.example.com chronyd[820]: Selected source 89.221.218.101
Nov 30 14:39:26 demo.example.com chronyd[820]: Source 108.61.164.200 replaced with 5.79.75.37
Nov 30 15:01:14 demo.example.com chronyd[820]: Selected source 147.251.48.140
Dec 01 07:18:26 demo.example.com chronyd[820]: Forward time jump detected!
Dec 01 07:18:26 demo.example.com chronyd[820]: Can\'t synchronise: no selectable sources
Dec 01 07:20:35 demo.example.com chronyd[820]: Selected source 147.251.48.140
Dec 01 07:23:49 demo.example.com chronyd[820]: Selected source 89.234.64.77
Dec 01 07:33:31 demo.example.com chronyd[820]: Selected source 89.221.218.101
[root@demo devops]#
after execution
$ ssh devops@demo.example.com
Last login: Wed Dec 1 08:39:40 2021 from 192.168.0.101
[devops@demo ~]$ sudo su
[root@demo devops]# systemctl status chronyd.service
● chronyd.service - NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Docs: man:chronyd(8)
man:chrony.conf(5)
Nov 30 15:01:14 demo.example.com chronyd[820]: Selected source 147.251.48.140
Dec 01 07:18:26 demo.example.com chronyd[820]: Forward time jump detected!
Dec 01 07:18:26 demo.example.com chronyd[820]: Can\'t synchronise: no selectable sources
Dec 01 07:20:35 demo.example.com chronyd[820]: Selected source 147.251.48.140
Dec 01 07:23:49 demo.example.com chronyd[820]: Selected source 89.234.64.77
Dec 01 07:33:31 demo.example.com chronyd[820]: Selected source 89.221.218.101
Dec 01 08:39:23 demo.example.com systemd[1]: Stopping NTP client/server...
Dec 01 08:39:23 demo.example.com chronyd[820]: chronyd exiting
Dec 01 08:39:23 demo.example.com systemd[1]: chronyd.service: Succeeded.
Dec 01 08:39:23 demo.example.com systemd[1]: Stopped NTP client/server.
[root@demo devops]# reboot
Connection to demo.example.com closed by remote host.
Connection to demo.example.com closed.
ansible-pilot $ ssh devops@demo.example.com
Last login: Wed Dec 1 08:40:41 2021 from 192.168.0.101
[devops@demo ~]$ sudo su
[root@demo devops]# uptime
08:41:08 up 0 min, 1 user, load average: 0.28, 0.06, 0.02
[root@demo devops]# systemctl status chronyd.service
● chronyd.service - NTP client/server
Loaded: loaded (/usr/lib/systemd/system/chronyd.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Docs: man:chronyd(8)
man:chrony.conf(5)
[root@demo devops]#
See also: ansible.builtin.service: Manage Services with Ansible (Complete Guide)
Conclusion
Now you know how to stop and disable services on boot on Linux remote hosts with Ansible.Stop a Service
- ansible.builtin.service:
name: nginx
state: stopped
become: trueSee also: Deploy Squid Proxy on RedHat Systems with Ansible
Disable on Boot
- service:
name: nginx
enabled: false
become: trueStop AND Disable
- service:
name: nginx
state: stopped
enabled: false
become: trueUsing systemd Module
- ansible.builtin.systemd:
name: nginx
state: stopped
enabled: false
daemon_reload: true # Reload systemd if unit file changed
become: trueMask a Service (Prevent Starting)
- systemd:
name: nginx
masked: true
become: true
# Masked services can't be started even manuallyCheck Service Status First
- ansible.builtin.service_facts:
- debug:
msg: "nginx is {{ ansible_facts.services['nginx.service'].state }}"
when: "'nginx.service' in ansible_facts.services"
- service:
name: nginx
state: stopped
when:
- "'nginx.service' in ansible_facts.services"
- ansible_facts.services['nginx.service'].state == 'running'
become: trueStop Multiple Services
- service:
name: "{{ item }}"
state: stopped
enabled: false
loop:
- postfix
- cups
- avahi-daemon
- bluetooth
become: true
ignore_errors: true # Some may not be installedConditional by OS
- service:
name: "{{ item }}"
state: stopped
enabled: false
loop: "{{ unnecessary_services }}"
become: true
vars:
unnecessary_services: >-
{{ (ansible_os_family == 'Debian') |
ternary(['cups', 'avahi-daemon'],
['cups', 'avahi-daemon', 'firewalld']) }}Security Hardening Pattern
- name: Disable unnecessary services
service:
name: "{{ item }}"
state: stopped
enabled: false
loop:
- telnet
- rsh
- rlogin
- cups
- avahi-daemon
- bluetooth
- postfix
become: true
ignore_errors: true
tags: [hardening]Timer Units (systemd)
# Disable a timer
- systemd:
name: apt-daily.timer
state: stopped
enabled: false
become: trueFAQ
service vs systemd module?
service works on any init system (systemd, SysV, Upstart). systemd is systemd-specific but supports extra features like daemon_reload and masked.
How to check if a service exists before managing it?
Use service_facts and check ansible_facts.services. Or use ignore_errors: true if the service might not be installed.
What's the difference between disabled and masked?
Disabled: won't start on boot but can be started manually. Masked: can't be started at all (linked to /dev/null).
Related Articles
Category: installation
Watch the video: Ansible Stop & Disable Service: systemd Module Guide (with Examples) — Video Tutorial