Once you can read service units, the next step is creating your own. systemd gives you reliable execution, restart policies, and scheduling tools that are usually cleaner than ad hoc shell wrappers.
Table of Contents
This post walks through custom units, timers, socket activation, and validation workflows.
Custom Services
Timers and Activation
- Replace cron-style jobs with timers
- Understand socket activation basics
- Use path units for file-triggered execution
- Mount and automount pointers
Series Navigation
Create a custom service unit
Start with a simple service that runs a script as a dedicated user.
# /etc/systemd/system/hello-app.service
[Unit]
Description=Hello app background worker
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=app
Group=app
WorkingDirectory=/opt/hello-app
ExecStart=/opt/hello-app/bin/worker.sh
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.targetThis unit starts after network readiness and automatically retries on failure.
Install and start the unit
After adding the file, reload metadata and activate it.
sudo systemctl daemon-reload
sudo systemctl enable --now hello-app.service
systemctl status hello-app.serviceIf configuration changes later, run daemon-reload again before restart or reload actions.
Validate and troubleshoot
Validate syntax and inspect logs before promoting changes.
sudo systemd-analyze verify /etc/systemd/system/hello-app.service
journalctl -u hello-app.service -b -n 100Quick troubleshooting checklist:
- Confirm executable paths and permissions for
ExecStart - Confirm user and group exist on the host
- Confirm required network or filesystem dependencies are declared
Replace cron-style jobs with timers
systemd timers can replace many cron jobs and provide stronger unit state visibility.
Service file:
# /etc/systemd/system/db-backup.service
[Unit]
Description=Run database backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/db-backup.shTimer file:
# /etc/systemd/system/db-backup.timer
[Unit]
Description=Schedule database backup
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.targetEnable timer:
sudo systemctl daemon-reload
sudo systemctl enable --now db-backup.timer
systemctl list-timers --allPersistent=true runs missed executions after downtime, which is often better than basic cron behavior.
Understand socket activation basics
Socket activation allows systemd to listen on a socket and start the backing service only when traffic arrives.
Socket unit:
# /etc/systemd/system/demo.socket
[Unit]
Description=Demo socket listener
[Socket]
ListenStream=127.0.0.1:9090
[Install]
WantedBy=sockets.targetService unit:
# /etc/systemd/system/demo.service
[Unit]
Description=Demo on-demand service
[Service]
ExecStart=/usr/local/bin/demo-serverBenefits:
- Lower idle resource usage for infrequent workloads
- Faster perceived startup because listeners are ready early
- Consistent unit visibility through systemd state
Use path units for file-triggered execution
Path units watch filesystem paths and trigger services when files appear or change.
# /etc/systemd/system/import.path
[Unit]
Description=Watch import inbox
[Path]
PathChanged=/var/lib/import/inbox
[Install]
WantedBy=multi-user.targetPath units are useful for lightweight event-driven automation without custom polling loops.
Mount and automount pointers
systemd can also manage mount lifecycle through mount and automount units, which is useful for network filesystems and delayed mount behavior.
Use this pattern when:
- Startup should not block waiting for slow remote mounts
- Access should trigger mount on demand
- Mount behavior should be managed with explicit dependencies
Series navigation
You now have the tools to author and schedule custom workloads, and the next step is networking with systemd-networkd.
Next in this series
Next, we configure networking declaratively with systemd-networkd, including interface matching, static addresses, and DHCP.
systemd-networkd: Declarative Network Configuration on Linux