I spent years avoiding systemd. init.d scripts felt familiar, cron was "good enough", and systemctl felt like it was designed to be unreadable. Then I actually read the documentation and realized I'd been fighting the very thing that makes Linux services manageable.

Here's what I wish someone had told me ten years ago.

Why systemd Won

systemd isn't just an init system. It's a service manager, a logger, a timer, a network configurator, and about twenty other things. Most of those are worth ignoring ( I'm looking at you, systemd-resolved ). But the service management and the timer system? Those are genuinely better than what came before.

The thing that sold me: dependency ordering. With init.d, you'd write sleep 5 in your startup script and pray. With systemd, you say After=network.target and it actually waits for the network to be up. No more race conditions at 3 AM.

Developer at terminal
The face I make when someone says "just use cron".

Writing a Service File

Here's a real service file I use for a Python API on a VPS. This is the whole thing.

[Unit]
Description=My Python API
After=network.target

[Service]
Type=simple
User=api
WorkingDirectory=/opt/api
ExecStart=/opt/api/venv/bin/python -m api.main
Restart=on-failure
RestartSec=5
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target

Save that to /etc/systemd/system/api.service, then:

sudo systemctl daemon-reload
sudo systemctl enable api.service
sudo systemctl start api.service

enable makes it start on boot. start starts it now. Two commands and your service is running with automatic restarts, proper logging, and boot ordering.

The Fields That Actually Matter

Most service files I see try to use every option in the man page. Don't. Here's what I actually use in production:

• Type=simple — the process doesn't fork. This covers 90% of use cases

• Restart=on-failure — auto-restart on crash, but not on clean exit

• RestartSec=5 — don't spam-restart a dying service. Give it a breath

• User= — never run services as root. Ever

• WorkingDirectory= — saves you from path confusion

• Environment= — pass config without config files

• After= / Wants= — declare your dependencies explicitly

Server infrastructure
Your services, managed properly. ( Finally. )

Checking What's Broken

When a service won't start, don't guess. Use the tools.

# Check if it's running
systemctl status api.service

# See the last 50 lines of output
journalctl -u api.service -n 50

# Follow logs in real time
journalctl -u api.service -f

# See why it failed
systemctl status api.service -l

The -l flag on status shows the full output instead of truncating it. I use this daily. journalctl is probably the single most useful tool systemd brought us — before systemd, you'd tail a log file and hope it was in the right place. Now everything is in one system.

systemd Timers: Cron But Better

I replaced all my cron jobs with systemd timers last year. No regrets. Here's why:

1. Timers show up in systemctl list-timers — no more grepping crontab

2. Each timer has its own log stream in journalctl

3. You can see when a timer last ran and when it'll run next

4. Missed executions can be caught up automatically

5. Timer files are in /etc/systemd/system/ — version controlled, deployable, readable

Here's a timer that runs a backup script every day at 3 AM:

# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup timer

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target

The .service file for the actual script:

# /etc/systemd/system/backup.service
[Unit]
Description=Daily backup

[Service]
Type=oneshot
ExecStart=/opt/backup/backup.sh

Type=oneshot because the script runs, finishes, and that's it. Persistent=true means if the machine was off at 3 AM, it'll run the backup as soon as it boots.

Computer screen with code
Every Linux box has been running systemd since 2015. Time to learn it.

Socket Activation

This is the systemd feature nobody talks about but everyone should use. Instead of keeping a service running 24/7, systemd listens on the port for you and starts the service only when a connection comes in.

# /etc/systemd/system/api.socket
[Unit]
Description=API socket

[Socket]
ListenStream=8080

[Install]
WantedBy=sockets.target

Your service file stays the same, but now it only starts when someone actually connects to port 8080. Saves memory. Saves CPU. Gives you that warm feeling of doing things properly.

The Commands I Use Every Week

# Start, stop, restart
systemctl start api.service
systemctl stop api.service
systemctl restart api.service

# Enable/disable on boot
systemctl enable api.service
systemctl disable api.service

# Check what failed on boot
systemctl --failed

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

# Edit a service without touching the file
systemctl edit api.service

# See what a service depends on
systemctl list-dependencies api.service

systemctl edit is underrated — it opens an override file, so your changes survive package updates.

Conclusion

systemd is not going away. It's been the default on every major distro for a decade. The documentation is actually good ( man systemd.service, man systemd.timer, man journalctl ). The syntax is consistent. The tooling is solid. Stop writing init.d scripts and cron jobs — learn the service files, learn the timers, learn journalctl. Your future self will thank you.