Creating custom systemd service files

dos2unix

Well-Known Member
Joined
May 3, 2019
Messages
4,408
Reaction score
4,601
Credits
41,638
I've done this article before (It's actually my most viewed article of all). But here we go again with the updated version.


Systemd Service Files: A Practical Guide
Creating custom systemd services is a common task for Linux administrators. This guide demonstrates service file creation through two examples: a simple time logger and a more complex SSH monitoring service.

Service File Basics
Systemd service files live in /etc/systemd/system/ and consist of three main sections:

[Unit] - Description and dependencies
[Service] - How the service runs
[Install] - When/how to enable it


Example 1: Simple Time Logger
Let's create a service that logs the current time every hour.
The Script:
Code:
#!/bin/bash
/usr/local/bin/timelog.sh
LOGFILE="/var/log/timelog.log"
echo "(date′+(date '+%Y-%m-%d %H:%M:%S') - Service executed" >> "
(date′+LOGFILE"

Make it executable:
Code:
chmod +x /usr/local/bin/timelog.sh
The Service File:
Code:
/etc/systemd/system/timelog.service
[Unit]
Description=Time Logger Service
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/timelog.sh
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
The Timer File:
Code:
/etc/systemd/system/timelog.timer
[Unit]
Description=Run Time Logger every hour
Requires=timelog.service
[Timer]
OnBootSec=5min
OnUnitActiveSec=1h
Unit=timelog.service
[Install]
WantedBy=timers.target
Managing the Service:
Code:
Reload systemd to recognize new files
sudo systemctl daemon-reload
Enable and start the timer
sudo systemctl enable timelog.timer
sudo systemctl start timelog.timer
Check status
sudo systemctl status timelog.timer
sudo systemctl status timelog.service
View logs
sudo journalctl -u timelog.service -f
Stop and disable
sudo systemctl stop timelog.timer
sudo systemctl disable timelog.timer
 


Example 2: SSH Login Monitor (Advanced)​


A more practical example that monitors SSH login attempts and logs suspicious activity.


The Script:
Code:
#!/bin/bash


[HEADING=2]/usr/local/bin/sshmon.sh[/HEADING]

LOGFILE="/var/log/sshmon.log"AUTH_LOG="/var/log/auth.log"ALERT_FILE="/var/log/sshmon_alerts.log"


[HEADING=2]Create log file if doesn't exist[/HEADING]

touch "$LOGFILE"


[HEADING=2]Monitor auth.log for failed SSH attempts[/HEADING]

tail -n0 -F "$AUTH_LOG" | while read line; do    if echo "$line" | grep -q "Failed password"; then        echo "(date′+(date '+%Y-%m-%d %H:%M:%S') - ALERT: $line" >> "(date′+ALERT_FILE"        echo "(date′+(date '+%Y-%m-%d %H:%M:%S') - Failed login detected" >> "(date′+LOGFILE"    fi
 
Example 2: SSH Login Monitor (Advanced)

A more practical example that monitors SSH login attempts and logs suspicious activity.

The Script:
Code:
#!/bin/bash
# /usr/local/bin/sshmon.sh

LOGFILE="/var/log/sshmon.log"
AUTH_LOG="/var/log/auth.log"
ALERT_FILE="/var/log/sshmon_alerts.log"

# Create log file if doesn't exist
touch "$LOGFILE"

# Monitor auth.log for failed SSH attempts
tail -n0 -F "$AUTH_LOG" | while read line; do
    if echo "$line" | grep -q "Failed password"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - ALERT: $line" >> "$ALERT_FILE"
        echo "$(date '+%Y-%m-%d %H:%M:%S') - Failed login detected" >> "$LOGFILE"
    fi
    
    if echo "$line" | grep -q "Accepted password"; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') - INFO: $line" >> "$LOGFILE"
    fi
done

Make it executable:
Code:
chmod +x /usr/local/bin/sshmon.sh

Create dedicated user (security best practice):
Code:
sudo useradd -r -s /bin/false sshmon
sudo touch /var/log/sshmon.log /var/log/sshmon_alerts.log
sudo chown sshmon:sshmon /var/log/sshmon*.log

The Service File (with advanced features):
Code:
# /etc/systemd/system/sshmon.service

[Unit]
Description=SSH Login Monitor
Documentation=man:sshd(8)
After=network.target syslog.target ssh.service
Wants=network.target
Requires=ssh.service

[Service]
Type=simple
User=sshmon
Group=sshmon
ExecStartPre=/bin/touch /var/log/sshmon.log
ExecStart=/usr/local/bin/sshmon.sh
Restart=on-failure
RestartSec=10s
StandardOutput=journal
StandardError=journal
WorkingDirectory=/var/log

# Security hardening
PrivateTmp=yes
NoNewPrivileges=true
ReadOnlyPaths=/etc /usr
ReadWritePaths=/var/log

[Install]
WantedBy=multi-user.target

Managing the SSH Monitor:
Code:
# Reload systemd
sudo systemctl daemon-reload

# Enable to start on boot
sudo systemctl enable sshmon.service

# Start the service
sudo systemctl start sshmon.service

# Check status and recent logs
sudo systemctl status sshmon.service

# View live logs
sudo journalctl -u sshmon.service -f

# View alert log
sudo tail -f /var/log/sshmon_alerts.log

# Restart the service
sudo systemctl restart sshmon.service

# Stop the service
sudo systemctl stop sshmon.service

# Disable from starting on boot
sudo systemctl disable sshmon.service
 
Common Service Directives Explained

[Unit] Section:
Code:
Description=       # Human-readable service description
After=             # Start after these services
Before=            # Start before these services
Requires=          # Hard dependency (fails if dependency fails)
Wants=             # Soft dependency (continues if dependency fails)
Documentation=     # Link to man pages or URLs

[Service] Section:
Code:
Type=              # Service type:
                   #   simple - main process (default)
                   #   forking - forks and parent exits
                   #   oneshot - runs once and exits
                   #   notify - notifies systemd when ready

ExecStart=         # Command to start service
ExecStartPre=      # Command to run before start
ExecStartPost=     # Command to run after start
ExecStop=          # Command to stop service
ExecReload=        # Command to reload config

User=              # Run as this user
Group=             # Run as this group
WorkingDirectory=  # Set working directory

Restart=           # Restart policy:
                   #   no - never restart
                   #   on-failure - restart on error
                   #   always - always restart
                   #   on-abnormal - restart on signal/timeout

RestartSec=        # Delay before restart (default 100ms)

Environment=       # Set environment variables
EnvironmentFile=   # Load variables from file

StandardOutput=    # Where stdout goes (journal, file, null)
StandardError=     # Where stderr goes

# Security options
PrivateTmp=        # Use private /tmp
NoNewPrivileges=   # Prevent privilege escalation
ReadOnlyPaths=     # Make paths read-only
ReadWritePaths=    # Allow write access to paths

[Install] Section:
Code:
WantedBy=          # Target that wants this service:
                   #   multi-user.target - normal services
                   #   graphical.target - GUI services
                   #   timers.target - for timers

RequiredBy=        # Target that requires this service
Alias=             # Alternative names for service

---

## Troubleshooting Tips

Check service status:
Code:
sudo systemctl status servicename.service

View recent logs:
Code:
sudo journalctl -u servicename.service -n 50

Follow logs in real-time:
Code:
sudo journalctl -u servicename.service -f

View logs since specific time:
Code:
sudo journalctl -u servicename.service --since "2026-01-02 10:00:00"

Check for errors:
Code:
sudo journalctl -u servicename.service -p err

Verify service file syntax:
Code:
sudo systemd-analyze verify /etc/systemd/system/servicename.service

List all failed services:
Code:
sudo systemctl --failed

Show dependency tree:
Code:
systemctl list-dependencies servicename.service

---

## Common Issues

Service won't start:
  • Check permissions on script and log files
  • Verify script has execute bit (chmod +x)
  • Check for syntax errors in service file
  • Review journalctl for error messages

Service starts but immediately stops:
  • For Type=simple, ensure script doesn't exit immediately
  • Check Restart= policy
  • Verify dependencies are met
  • Review exit codes in journalctl

Permission errors:
  • Ensure User/Group has access to required files
  • Check SELinux/AppArmor policies
  • Verify log directory permissions

---

This guide covers the fundamentals of systemd service creation. For more advanced features, consult man systemd.service and man systemd.unit.
 
Good read, Ray. :)
 
(modified a bit by @wendy-lebaron (me):

Managing the Service:
Code:
# Reload systemd to recognize new files
sudo systemctl daemon-reload
# Enable and start the timer
sudo systemctl enable timelog.timer
sudo systemctl start timelog.timer
# Check status
sudo systemctl status timelog.timer
sudo systemctl status timelog.service
# View logs
sudo journalctl -u timelog.service -f
# Stop and disable
sudo systemctl stop timelog.timer
sudo systemctl disable timelog.timer

@dos2unix

thank you for this article. it is insightful. good job on this.

however you might have to format a few things. like when you put stuff in "code" block. because at least one person is going to copy-paste. the entire "code" block. then try to run it on his/her "shell." then get frustrated at the error messages. or try to run it all at once. where it wasn't intended. "it doesn't work!" that person might clamor.

edit: this was before i saw post #4 of this thread.

it's just a suggestion.
 


Follow Linux.org

Members online


Top