- Fixed 4 broken markdown links (bad relative paths in See Also sections) - Corrected n8n port binding to 127.0.0.1:5678 (matches actual deployment) - Updated SnapRAID article with actual majorhome paths (/majorRAID, disk1-3) - Converted 67 Obsidian wikilinks to relative markdown links or plain text - Added YAML frontmatter to 35 articles missing it entirely - Completed frontmatter on 8 articles with missing fields Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
151 lines
4.3 KiB
Markdown
151 lines
4.3 KiB
Markdown
---
|
|
title: "Managing Linux Services: systemd and Ansible"
|
|
domain: linux
|
|
category: process-management
|
|
tags: [systemd, ansible, services, linux, automation]
|
|
status: published
|
|
created: 2026-03-08
|
|
updated: 2026-03-08
|
|
---
|
|
|
|
# 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
|
|
|
|
```bash
|
|
# 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:**
|
|
|
|
```bash
|
|
# 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:**
|
|
|
|
```bash
|
|
# 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.
|
|
|
|
```ini
|
|
[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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```yaml
|
|
- name: Ensure nginx is started and enabled
|
|
ansible.builtin.service:
|
|
name: nginx
|
|
state: started
|
|
enabled: true
|
|
```
|
|
|
|
A more complete playbook pattern:
|
|
|
|
```yaml
|
|
---
|
|
- 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:
|
|
|
|
```bash
|
|
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](../distro-specific/wsl2-instance-migration-fedora43.md)
|
|
- [tuning-netdata-web-log-alerts](../../02-selfhosting/monitoring/tuning-netdata-web-log-alerts.md)
|