Skip to content

Managing Linux Services: systemd and Ansible

If you're running services on a Linux server, systemd is what you're working with day-to-day — starting, stopping, restarting, and checking on things. For managing services across multiple machines, Ansible is where I've landed. It handles the repetitive stuff so you can focus on what actually matters.

The Short Answer

# Check a service status
systemctl status servicename

# Start / stop / restart
sudo systemctl start servicename
sudo systemctl stop servicename
sudo systemctl restart servicename

# Enable at boot
sudo systemctl enable servicename

# Disable at boot
sudo systemctl disable servicename

# Reload config without full restart (if supported)
sudo systemctl reload servicename

systemd Basics

systemd is the init system on basically every major Linux distro now. Love it or not, it's what you're using. The systemctl command is your interface to it.

Checking what's running:

# List all active services
systemctl list-units --type=service --state=active

# List failed services (run this when something breaks)
systemctl list-units --type=service --state=failed

Reading logs for a service:

# Last 50 lines
journalctl -u servicename -n 50

# Follow live (like tail -f)
journalctl -u servicename -f

# Since last boot
journalctl -u servicename -b

journalctl is your friend. When a service fails, go here before you do anything else.

Writing a simple service file:

Drop a .service file in /etc/systemd/system/ and systemd will pick it up.

[Unit]
Description=My Custom App
After=network.target

[Service]
Type=simple
User=myuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/start.sh
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

After creating or editing a service file, reload the daemon before doing anything else:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp

Ansible for Service Management Across Machines

For a single box, systemctl is fine. Once you've got two or more servers doing similar things, Ansible starts paying for itself fast. I've been using it heavily at work and it's changed how I think about managing infrastructure.

The ansible.builtin.service module handles start/stop/enable/disable:

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

A more complete playbook pattern:

---
- name: Manage web services
  hosts: webservers
  become: true

  tasks:
    - name: Install nginx
      ansible.builtin.package:
        name: nginx
        state: present

    - name: Start and enable nginx
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true

    - name: Reload nginx after config change
      ansible.builtin.service:
        name: nginx
        state: reloaded

Run it with:

ansible-playbook -i inventory.ini manage-services.yml

Gotchas & Notes

  • daemon-reload is mandatory after editing service files. Forgetting this is the most common reason changes don't take effect.
  • restart vs reload: restart kills and relaunches the process. reload sends SIGHUP and asks the service to re-read its config without dropping connections — only works if the service supports it. nginx and most web servers do. Not everything does.
  • Ansible's restarted vs reloaded state: Same distinction applies. Use reloaded in Ansible handlers when you're pushing config changes to a running service.
  • Checking if a service is masked: A masked service can't be started at all. systemctl status servicename will tell you. Unmask with sudo systemctl unmask servicename.
  • On Fedora/RHEL: SELinux can block a custom service from running even if systemd says it started fine. If you see permission errors in journalctl, check ausearch -m avc for SELinux denials.

See Also

  • [[wsl2-instance-migration-fedora43]]
  • [[tuning-netdata-web-log-alerts]]