What Is the ansible service Module?

The ansible service module (ansible.builtin.service) manages system services — start, stop, restart, reload, and enable services for boot. It works across init systems (systemd, SysV, Upstart). For systemd-specific features, use ansible.builtin.systemd.

Use this module when you want a portable service task that works across Linux init systems; switch to ansible.builtin.systemd only when you need systemd-only options such as daemon_reload, masked, or scope.

Basic Usage

# Start a service
- name: Start nginx
  ansible.builtin.service:
    name: nginx
    state: started

# Stop a service
- name: Stop nginx
  ansible.builtin.service:
    name: nginx
    state: stopped

# Restart a service
- name: Restart nginx
  ansible.builtin.service:
    name: nginx
    state: restarted

# Reload configuration (no downtime)
- name: Reload nginx
  ansible.builtin.service:
    name: nginx
    state: reloaded

Start and Enable at Boot

# Start now AND enable at boot
- name: Ensure nginx is running and enabled
  ansible.builtin.service:
    name: nginx
    state: started
    enabled: true

# Enable at boot without starting now
- name: Enable service at boot
  ansible.builtin.service:
    name: nginx
    enabled: true

# Disable from boot
- name: Disable service at boot
  ansible.builtin.service:
    name: nginx
    enabled: false

Parameters

ParameterDescription
nameService name (required)
statestarted, stopped, restarted, reloaded
enabledStart at boot: true/false
patternProcess name pattern (for SysV init detection)
sleepSeconds to sleep between stop and start (for restarted)

service vs systemd

# service — works on any init system
- ansible.builtin.service:
    name: nginx
    state: started

# systemd — systemd-specific features
- ansible.builtin.systemd:
    name: nginx
    state: started
    daemon_reload: true     # Reload systemd after unit file change
    scope: system           # system, user, or global
    no_block: true          # Don't wait for completion
    masked: false           # Unmask a service

Use service for portability. Use systemd when you need daemon_reload, masked, or scope.

Common Patterns

Deploy Config and Restart

---
- name: Deploy web server
  hosts: webservers
  become: true
  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present

    - name: Deploy config
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
        validate: "nginx -t -c %s"
      notify: Reload nginx      # Only restart when config changes

    - name: Ensure nginx is running
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

  handlers:
    - name: Reload nginx
      ansible.builtin.service:
        name: nginx
        state: reloaded

Manage Multiple Services

- name: Ensure all services are running
  ansible.builtin.service:
    name: "{{ item }}"
    state: started
    enabled: true
  loop:
    - nginx
    - redis-server
    - postgresql

- name: Stop unnecessary services
  ansible.builtin.service:
    name: "{{ item }}"
    state: stopped
    enabled: false
  loop:
    - apache2
    - sendmail
    - cups
  ignore_errors: true    # Don't fail if service doesn't exist

Check Service Status Before Acting

- name: Check if nginx is running
  ansible.builtin.command: systemctl is-active nginx
  register: nginx_status
  ignore_errors: true
  changed_when: false

- name: Start nginx if not running
  ansible.builtin.service:
    name: nginx
    state: started
  when: nginx_status.rc != 0

- name: Show status
  ansible.builtin.debug:
    msg: "nginx is {{ 'running' if nginx_status.rc == 0 else 'stopped' }}"

Deploy Custom systemd Service

- name: Deploy systemd service file
  ansible.builtin.template:
    src: myapp.service.j2
    dest: /etc/systemd/system/myapp.service
    mode: "0644"
  notify:
    - Reload systemd
    - Restart myapp

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

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

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

templates/myapp.service.j2:

[Unit]
Description={{ app_name }}
After=network.target

[Service]
Type=simple
User={{ app_user }}
WorkingDirectory={{ app_dir }}
ExecStart={{ app_dir }}/bin/start --port {{ app_port }}
Restart=always
RestartSec=5
Environment=APP_ENV={{ app_env }}

[Install]
WantedBy=multi-user.target

Rolling Restart

# Restart services one host at a time
- name: Rolling restart
  hosts: webservers
  serial: 1              # One host at a time
  become: true
  tasks:
    - name: Restart application
      ansible.builtin.service:
        name: myapp
        state: restarted

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

Mask/Unmask Services (systemd)

# Mask a service (prevent it from starting)
- name: Mask apache2
  ansible.builtin.systemd:
    name: apache2
    masked: true

# Unmask
- name: Unmask apache2
  ansible.builtin.systemd:
    name: apache2
    masked: false

state Behavior

StateAction
startedStart if stopped; no action if already running
stoppedStop if running; no action if already stopped
restartedAlways stops and starts (even if running)
reloadedReload config without full restart

started and stopped are idempotent. restarted always causes a change — use handlers to restart only when needed.

Troubleshooting

"Could not find the requested service"

Service name might differ:

# Find the actual service name
systemctl list-units --type=service | grep nginx
# Output: nginx.service

# Some services have different names
# RedHat: httpd     vs  Debian: apache2
# RedHat: mariadb   vs  Debian: mysql

"Unable to restart service: daemon-reload required"

After changing a unit file, reload systemd:

- ansible.builtin.systemd:
    daemon_reload: true

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

Service Starts but Immediately Stops

Check the service logs:

- name: Check service logs
  ansible.builtin.command: journalctl -u myapp --no-pager -n 20
  register: logs

- debug:
    var: logs.stdout_lines

Conclusion

ansible.builtin.service handles service lifecycle: start, stop, restart, reload, and boot enablement. Use state: started + enabled: true for services that should always be running. Use handlers with notify to restart only when configuration changes. For systemd-specific features (daemon_reload, masking, scoping), use ansible.builtin.systemd. Prefer reloaded over restarted when possible — it applies config changes without downtime.