Chapter 8.2 - Custom Services
Learning Objectives
By the end of this chapter, you will be able to:
- Create a systemd unit file
- Configure a custom service
- Understand the sections of a unit file
- Manage dependencies
- Create services for scripts
Structure of a Unit File
Location
# System
/etc/systemd/system/
# User
~/.config/systemd/user/
Priority:
/etc/systemd/system/(system) >/usr/lib/systemd/system/(packages)
Basic Format
[Unit]
Description=Service description
After=network.target
[Service]
Type=simple
ExecStart=/path/to/command
Restart=always
[Install]
WantedBy=multi-user.target
Main Sections
[Unit]
[Unit]
Description=My Custom Service
Documentation=man:my-service(1)
After=network.target sshd.service
Before=some-service.service
Requires=network.target
Wants=sshd.service
Conflicts=old-service.service
Directives:
- Description: Service description
- After: Starts after these units
- Before: Starts before these units
- Requires: Strong dependency (fails if dependency fails)
- Wants: Weak dependency (continues if dependency fails)
- Conflicts: Cannot run simultaneously
[Service]
[Service]
Type=simple
ExecStart=/usr/local/bin/my-script.sh
ExecStartPre=/bin/echo "Starting"
ExecStartPost=/bin/echo "Started"
ExecStop=/bin/echo "Stopping"
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=10
User=myuser
Group=mygroup
WorkingDirectory=/var/lib/myservice
Environment="VAR1=value1"
EnvironmentFile=/etc/myservice/env
StandardOutput=journal
StandardError=journal
Types:
- simple: Main command (default)
- forking: Forks into background
- oneshot: Single execution
- notify: Notifies systemd when ready
Restart:
- no: Does not restart
- always: Always restart
- on-failure: Restart on failure
- on-success: Restart on success
[Install]
[Install]
WantedBy=multi-user.target
Alias=myservice.service
Common targets:
- multi-user.target: Multi-user mode
- graphical.target: Graphical mode
- default.target: Default target
Practical Examples
Simple Service
# /etc/systemd/system/my-service.service
[Unit]
Description=My Simple Service
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my-script.sh
Restart=always
RestartSec=10
User=www-data
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Enable:
sudo systemctl daemon-reload
sudo systemctl enable my-service
sudo systemctl start my-service
Service with Script
#!/bin/bash
# /usr/local/bin/my-script.sh
# Service script
# Cleanup function
cleanup() {
echo "Stopping service"
# Cleanup...
exit 0
}
# Trap signals
trap cleanup SIGTERM SIGINT
# Main loop
while true; do
# Work...
echo "Service active: $(date)"
sleep 60
done
Unit file:
[Unit]
Description=Service with Script
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my-script.sh
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Forking Service
[Unit]
Description=Forking Service
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/bin/daemon --daemonize
PIDFile=/var/run/daemon.pid
Restart=always
[Install]
WantedBy=multi-user.target
Oneshot Service
[Unit]
Description=Oneshot Script
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/init-script.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Environment Variables
In Unit File
[Service]
Environment="VAR1=value1"
Environment="VAR2=value2"
EnvironmentFile=/etc/myservice/env
Environment File
# /etc/myservice/env
VAR1=value1
VAR2=value2
VAR3="value with spaces"
Log Management
systemd Journal
# View service logs
sudo journalctl -u my-service
# Follow logs
sudo journalctl -u my-service -f
# Recent logs
sudo journalctl -u my-service -n 50
# Since date
sudo journalctl -u my-service --since "2024-01-01"
Redirection
[Service]
# To journal (default)
StandardOutput=journal
StandardError=journal
# To file
StandardOutput=file:/var/log/myservice.log
StandardError=file:/var/log/myservice-error.log
# Ignore
StandardOutput=null
StandardError=null
Best Practices
Structure
[Unit]
# Clear description
# Appropriate dependencies
[Service]
# Correct type
# Non-root User/Group if possible
# Appropriate restart
# Logging configured
[Install]
# Appropriate target
Security
[Service]
# Avoid using root if possible
User=serviceuser
Group=servicegroup
# Limit capabilities
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true
# Chroot if possible
RootDirectory=/var/lib/myservice
Summary
In this chapter, you learned:
Unit files: Structure [Unit], [Service], [Install]
Types: simple, forking, oneshot, notify
Dependencies: After, Before, Requires, Wants
Restart: always, on-failure, never
Environment: Environment, EnvironmentFile
Logs: journalctl, file redirection
Practice: Create services for scripts and applications
Next Steps
Chapter 8.3: Advanced Cron and systemd Timers
Module 9: System Security
Chapter created on: December 2024