Skip to content

Systemd Module — Managing Services


Every single “Restart app” handler in this course — going all the way back to the very first notify example, through the blocks and handlers section’s real-world patterns, right up through last chapter’s config-deployment example — has been ansible.builtin.debug, printing a message rather than actually doing anything. This is where that placeholder finally becomes real.

The systemd Module

Both our lab hosts — ubuntu and fedora — run systemd, despite being different distributions with different package managers. That’s exactly why this module doesn’t need an os_family split the way apt/dnf did: systemd itself is the same underlying init system regardless of which distro sits on top of it.

- name: Ensure nginx is running
  ansible.builtin.systemd:
    name: nginx
    state: started

state: Controlling A Service’s Current Status

  • started — ensure the service is running. Idempotent: does nothing if it’s already running.
  • stopped — ensure the service is stopped.
  • restarted — stop and start it again, unconditionally, every time this task runs.
  • reloaded — ask the service to re-read its configuration without a full stop/start cycle, for services that support it.
- name: Restart nginx
  ansible.builtin.systemd:
    name: nginx
    state: restarted

enabled: Controlling Whether A Service Starts On Boot

This is a genuinely separate concern from state: — worth being explicit about, since conflating “is it running right now” with “will it start automatically on boot” is an easy early mistake:

- name: Ensure nginx starts on boot
  ansible.builtin.systemd:
    name: nginx
    enabled: true

A service can be currently running but not enabled for boot, or enabled for boot but currently stopped — state and enabled are two independent properties, and neither implies the other.

Combining state And enabled

The typical real pattern sets both together:

- name: Ensure nginx is running and enabled on boot
  ansible.builtin.systemd:
    name: nginx
    state: started
    enabled: true

Replacing Every “Restart App” Placeholder, For Real

Here’s the actual payoff. Every handler this entire course has shown you, doing real work at last:

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

Drop this in place of any debug-based “Restart app” handler from earlier in this course, and every notify:-triggered pattern already covered — change-detection, the loop-notification behavior, force_handlers, all of it — works exactly the same way, except it now genuinely restarts a service instead of printing a message about one.

daemon_reload: When You’ve Changed A Unit File

If a task modifies the unit file itself — not the application’s own config, but the systemd service definition — systemd needs to be told to reload its own internal knowledge of that file before a restart will correctly pick up the change. Combining directly with template from the previous chapter:

- name: Deploy custom systemd unit file
  ansible.builtin.template:
    src: myapp.service.j2
    dest: /etc/systemd/system/myapp.service
  notify: Reload systemd and restart app

handlers:
  - name: Reload systemd and restart app
    ansible.builtin.systemd:
      name: myapp
      state: restarted
      daemon_reload: true

daemon_reload: true handles the reload of systemd’s own unit-file cache; state: restarted in the same task then applies it.

A Gotcha: restarted Is Not The Same As reloaded

state: restarted fully stops and starts a service — any active connection to it drops, however briefly, during that window. state: reloaded asks a service to re-read its configuration without a full stop, and for a service that genuinely supports it (nginx is a common example), that means zero dropped connections. Defaulting to restarted out of habit — because it’s what most examples show — introduces unnecessary downtime for anything that would have handled reloaded gracefully instead. The right choice depends entirely on whether the specific service you’re managing actually implements a real reload; not every service does, and trying reloaded against one that doesn’t can behave unpredictably rather than simply failing cleanly.

Best Practices

  • Set state and enabled explicitly, together, when both genuinely matter — never assume one implies the other.
  • Put real systemd tasks in handlers, not the debug placeholders used for illustration throughout the rest of this course — this is the module that makes every earlier notify: example actually do something.
  • Use daemon_reload: true whenever a task changes the unit file itself, not just the application’s own configuration.
  • Prefer state: reloaded over state: restarted when the specific service you’re managing genuinely supports a graceful reload — check its own documented behavior rather than defaulting to a full restart out of habit.
Last updated on