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 systemd Module: Manage Services, Timers & Units (Guide)

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

How to manage systemd services with Ansible systemd module (ansible.builtin.systemd). Start, stop, enable, reload services, manage timers and units.

The ansible.builtin.systemd module (also available as ansible.builtin.systemd_service) manages systemd units — services, timers, sockets, and more. It's the preferred way to manage services on modern Linux systems (RHEL 7+, Ubuntu 16.04+, Debian 8+).

Basic Operations

Start a Service

- name: Start nginx
  ansible.builtin.systemd:
    name: nginx
    state: started

Stop a Service

- name: Stop nginx
  ansible.builtin.systemd:
    name: nginx
    state: stopped

Restart a Service

- name: Restart nginx
  ansible.builtin.systemd:
    name: nginx
    state: restarted

Reload a Service

- name: Reload nginx configuration
  ansible.builtin.systemd:
    name: nginx
    state: reloaded

Enable at Boot

- name: Enable and start nginx
  ansible.builtin.systemd:
    name: nginx
    state: started
    enabled: true

Disable and Stop

- name: Disable and stop nginx
  ansible.builtin.systemd:
    name: nginx
    state: stopped
    enabled: false

See also: Ansible 2.17.0-rc1: Elevating Automation with ‘Gallows Pole’

systemd vs service Module

| Feature | ansible.builtin.systemd | ansible.builtin.service | |---------|--------------------------|--------------------------| | systemd-specific | ✅ Full support | ❌ Basic only | | daemon_reload | ✅ Yes | ❌ No | | Mask/unmask | ✅ Yes | ❌ No | | Timers | ✅ Yes | ❌ No | | Scope (user/system) | ✅ Yes | ❌ No | | Cross-platform | ❌ systemd only | ✅ SysV, Upstart, systemd | | Use when | Modern Linux (systemd) | Multi-platform compatibility |

Daemon Reload

After modifying unit files, reload the systemd daemon:

- name: Deploy custom service file
  ansible.builtin.copy:
    src: myapp.service
    dest: /etc/systemd/system/myapp.service
    mode: '0644'

- name: Reload systemd and start service ansible.builtin.systemd: name: myapp state: started enabled: true daemon_reload: true

Reload Daemon Without Managing a Service

- name: Just reload systemd daemon
  ansible.builtin.systemd:
    daemon_reload: true

See also: Ansible uri Module: Make HTTP/REST API Calls from Playbooks (Guide)

Mask and Unmask Units

Masking prevents a service from being started at all (even manually):

- name: Mask bluetooth (prevent it from ever starting)
  ansible.builtin.systemd:
    name: bluetooth
    masked: true

- name: Unmask bluetooth ansible.builtin.systemd: name: bluetooth masked: false

Manage Timers

Systemd timers are the modern replacement for cron:

- name: Deploy timer unit
  ansible.builtin.copy:
    content: |
      [Unit]
      Description=Run backup daily

[Timer] OnCalendar=daily Persistent=true

[Install] WantedBy=timers.target dest: /etc/systemd/system/backup.timer mode: '0644'

- name: Deploy backup service unit ansible.builtin.copy: content: | [Unit] Description=Backup service

[Service] Type=oneshot ExecStart=/usr/local/bin/backup.sh dest: /etc/systemd/system/backup.service mode: '0644'

- name: Enable and start timer ansible.builtin.systemd: name: backup.timer state: started enabled: true daemon_reload: true

See also: Ansible wait_for Module: Wait for Conditions, Ports & Files (Guide)

User Services (--user scope)

Manage user-level systemd services:

- name: Enable user service
  ansible.builtin.systemd:
    name: my-user-app
    state: started
    enabled: true
    scope: user
  become: false

Deploy Custom Service Files

Complete Service Deployment Pattern

- name: Create systemd service for application
  ansible.builtin.copy:
    content: |
      [Unit]
      Description={{ app_name }} Application
      After=network.target postgresql.service
      Requires=postgresql.service

[Service] Type=simple User={{ app_user }} Group={{ app_group }} WorkingDirectory={{ app_dir }} ExecStart={{ app_dir }}/bin/start.sh ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure RestartSec=5 StandardOutput=journal StandardError=journal SyslogIdentifier={{ app_name }}

# Security hardening NoNewPrivileges=true ProtectSystem=strict ProtectHome=true ReadWritePaths={{ app_dir }}/data {{ app_dir }}/logs PrivateTmp=true

# Environment Environment=NODE_ENV=production Environment=PORT={{ app_port }} EnvironmentFile=-/etc/default/{{ app_name }}

[Install] WantedBy=multi-user.target dest: "/etc/systemd/system/{{ app_name }}.service" owner: root group: root mode: '0644' notify: reload and restart app

handlers: - name: reload and restart app ansible.builtin.systemd: name: "{{ app_name }}" state: restarted daemon_reload: true

Using Templates

- name: Deploy service from template
  ansible.builtin.template:
    src: myapp.service.j2
    dest: /etc/systemd/system/myapp.service
    mode: '0644'
  notify:
    - reload systemd
    - restart myapp

handlers: - name: reload systemd ansible.builtin.systemd: daemon_reload: true

- name: restart myapp ansible.builtin.systemd: name: myapp state: restarted

Gather Service Facts

- name: Get all service states
  ansible.builtin.service_facts:

- name: Show nginx status ansible.builtin.debug: msg: "nginx is {{ ansible_facts.services['nginx.service'].state }}" when: "'nginx.service' in ansible_facts.services"

- name: List all running services ansible.builtin.debug: msg: "{{ ansible_facts.services | dict2items | selectattr('value.state', 'equalto', 'running') | map(attribute='key') | list }}"

Wait for Service to Be Ready

- name: Start and enable application
  ansible.builtin.systemd:
    name: myapp
    state: started
    enabled: true

- name: Wait for application to be ready ansible.builtin.uri: url: "http://localhost:{{ app_port }}/health" status_code: 200 register: health retries: 30 delay: 5 until: health.status == 200

Real-World Patterns

Rolling Service Restart

- name: Rolling restart of web servers
  hosts: webservers
  serial: 1
  tasks:
    - name: Restart nginx
      ansible.builtin.systemd:
        name: nginx
        state: restarted

- name: Wait for nginx to be ready ansible.builtin.uri: url: "http://localhost/health" status_code: 200 retries: 10 delay: 3 until: result.status == 200 register: result

Multiple Related Services

- name: Manage application stack
  ansible.builtin.systemd:
    name: "{{ item }}"
    state: started
    enabled: true
  loop:
    - redis
    - postgresql
    - myapp-worker
    - myapp-web

Conditional Service Management

- name: Enable firewalld on RHEL
  ansible.builtin.systemd:
    name: firewalld
    state: started
    enabled: true
  when: ansible_os_family == 'RedHat'

- name: Enable ufw on Ubuntu ansible.builtin.systemd: name: ufw state: started enabled: true when: ansible_distribution == 'Ubuntu'

Troubleshooting

Check Service Status

- name: Check service status
  ansible.builtin.command: systemctl status myapp
  register: status
  changed_when: false
  failed_when: false

- name: Show status ansible.builtin.debug: msg: "{{ status.stdout_lines }}"

View Service Logs

- name: Get recent service logs
  ansible.builtin.command: journalctl -u myapp -n 50 --no-pager
  register: logs
  changed_when: false

- name: Show logs ansible.builtin.debug: msg: "{{ logs.stdout_lines }}"

FAQ

What is the difference between systemd and service modules?

The systemd module provides full systemd functionality: daemon_reload, mask/unmask, timers, user scope. The service module is cross-platform (works with SysV, Upstart, and systemd) but only supports basic start/stop/enable operations.

When do I need daemon_reload?

Use daemon_reload: true after creating, modifying, or deleting systemd unit files in /etc/systemd/system/. This is equivalent to running systemctl daemon-reload on the command line.

What is the difference between restart and reload?

restart stops and starts the service (new process). reload sends a signal (usually SIGHUP) telling the service to re-read its configuration without stopping. Not all services support reload.

How do I manage services on systems without systemd?

Use ansible.builtin.service instead — it auto-detects the init system (SysV, Upstart, systemd, OpenRC) and uses the appropriate backend.

How do I create a systemd service with Ansible?

Deploy a .service unit file to /etc/systemd/system/ using the copy or template module, then use the systemd module with daemon_reload: true and state: started to activate it.

Conclusion

The ansible.builtin.systemd module is the standard for service management on modern Linux: • Basic ops: state: started/stopped/restarted/reloadedBoot config: enabled: true/falseAfter unit changes: daemon_reload: truePrevent start: masked: trueTimers: Same module, specify timer unit name • Always use handlers for config-triggered restarts

Related Articles

Ansible Handlers: Trigger Actions on ChangeAnsible block, rescue, always: Error Handling GuideAnsible retries & until: Retry Failed TasksAnsible Cron Module: Schedule Tasks Guide

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home