funwithlinux guide

Systemd Timers: Automating Service Tasks Efficiently

In the world of Linux system administration, automation is key to maintaining efficiency, reliability, and consistency. For decades, tools like `cron` have been the go-to for scheduling repetitive tasks. However, with the rise of `systemd`—the init system and service manager adopted by most modern Linux distributions (e.g., Ubuntu, Fedora, Debian, Arch)—a powerful alternative has emerged: **systemd timers**. Systemd timers offer more flexibility, better integration with systemd’s ecosystem, and enhanced logging compared to traditional `cron`. They allow you to schedule tasks based on calendar events (e.g., "every Monday at 3 AM"), relative time (e.g., "10 minutes after boot"), or system events (e.g., "after the network is online"). Whether you’re automating backups, log rotations, or periodic maintenance, systemd timers provide a robust and modern solution. In this blog, we’ll dive deep into systemd timers: how they work, their components, how to create and manage them, advanced configurations, and real-world use cases. By the end, you’ll be equipped to replace or complement `cron` with systemd timers for more efficient task automation.

Table of Contents

  1. Introduction to Systemd Timers
  2. How Systemd Timers Work (vs. Cron)
  3. Components of a Systemd Timer
  4. Creating Your First Timer: A Step-by-Step Guide
  5. Advanced Timer Configuration
  6. Managing Timers: Commands and Best Practices
  7. Debugging and Troubleshooting Timers
  8. Real-World Use Cases
  9. Conclusion
  10. References

Introduction to Systemd Timers

Systemd timers are part of the systemd suite, designed to trigger systemd service units at specific times or intervals. Unlike cron, which uses a simple text-based crontab file, timers are defined as systemd units (.timer files) that work in tandem with service units (.service files). The timer unit acts as a scheduler, while the service unit contains the actual task to execute.

Key Advantages Over Cron:

  • Tight Integration with Systemd: Timers can depend on other systemd units (e.g., network-online.target for internet-dependent tasks) and leverage systemd features like cgroups, logging, and resource management.
  • Flexible Scheduling: Supports both calendar-based events (e.g., “every 1st of the month”) and monotonic (relative) time (e.g., “30 seconds after boot”).
  • Enhanced Logging: All timer and task output is captured by journald, making debugging easier with journalctl.
  • Missed Task Handling: The Persistent directive ensures tasks run even if the system was offline during the scheduled time.
  • Randomized Delays: Prevent “thundering herd” problems with RandomizedDelaySec to spread out task execution.

How Systemd Timers Work (vs. Cron)

To understand systemd timers, it helps to contrast them with cron:

FeatureCronSystemd Timers
ConfigurationText-based crontab files (/etc/crontab, crontab -e).Systemd unit files (.timer and .service).
SchedulingCalendar-based only (e.g., 0 3 * * * for 3 AM daily).Both calendar-based (OnCalendar) and monotonic (relative) time (OnBootSec, OnActiveSec).
DependenciesNone; tasks run independently.Can depend on systemd targets/services (e.g., After=network.target).
LoggingLimited; output sent to email or /var/log/cron.Integrated with journald; logs accessible via journalctl.
Missed TasksIgnored by default (unless using anacron for daily/weekly tasks).Persistent=true ensures missed tasks run on next boot.

Systemd timers bridge the gap between simple cron jobs and complex systemd service dependencies, making them ideal for modern Linux environments.

Components of a Systemd Timer

A systemd timer setup requires two files:

  1. A timer unit file (.timer): Defines when the task should run.
  2. A service unit file (.service): Defines what task to run (the actual command/script).

1. The Timer Unit File (.timer)

The timer unit file (e.g., backup.timer) contains scheduling logic. It uses three main sections: [Unit], [Timer], and [Install].

Example Timer File Structure:

# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup of user documents
Documentation=man:backup(1)

[Timer]
# Calendar event: Run daily at 3 AM
OnCalendar=*-*-* 03:00:00
# Wait 5 minutes after boot before first run (if missed)
OnBootSec=5min
# Add a random 1-5 minute delay to avoid server load spikes
RandomizedDelaySec=1min 5min
# Ensure missed tasks run on next boot
Persistent=true
# Accuracy: Allow 1 minute deviation from scheduled time (default: 1min)
AccuracySec=1min

[Install]
# Enable timer on boot by linking to multi-user.target
WantedBy=multi-user.target

Key [Timer] Directives:

  • OnCalendar: Calendar-based schedule (syntax below).
  • OnBootSec: Run task X seconds/minutes/hours after boot (e.g., OnBootSec=10min).
  • OnActiveSec: Run task X seconds after the timer itself is activated (e.g., OnActiveSec=1h).
  • RandomizedDelaySec: Add a random delay (e.g., RandomizedDelaySec=30s to 5min).
  • Persistent: If true, run missed tasks on next boot (default: false).
  • AccuracySec: Tolerance for schedule deviation (reduces CPU wake-ups; default: 1min).

2. The Service Unit File (.service)

The service unit file (e.g., backup.service) defines the task to execute when the timer triggers. It uses the [Unit], [Service], and [Install] sections (though [Install] is often optional for timer-driven services).

Example Service File:

# /etc/systemd/system/backup.service
[Unit]
Description=Backup user documents to /backup
Requires=mount-backup-drive.service  # Depend on backup drive being mounted
After=mount-backup-drive.service

[Service]
# Run as non-root user (safer than root)
User=john
Group=john
# Command to execute: Backup /home/john/docs to /backup/docs-YYYY-MM-DD
ExecStart=/bin/bash -c 'cp -r /home/john/docs /backup/docs-$(date +\%Y-\%m-\%d)'
# Ensure the task runs to completion even if the user logs out
Type=oneshot

Key [Service] Directives:

  • ExecStart: The command/script to run (required).
  • User/Group: Run the task as a specific user/group (avoid root unless necessary).
  • Type=oneshot: For tasks that run once and exit (default for timers).
  • Requires/After: Dependencies (e.g., ensure a backup drive is mounted first).

Creating Your First Timer: Step-by-Step

Let’s walk through creating a daily backup timer to automate backing up a user’s Documents folder to /backup.

Step 1: Create the Service File

First, define the backup task in a .service file. We’ll use backup.service:

sudo nano /etc/systemd/system/backup.service

Add the following content:

[Unit]
Description=Daily backup of Documents folder
Documentation=https://example.com/backup-guide

[Service]
User=john  # Replace with your username
Group=john
Type=oneshot
# Backup command: Copy Documents to /backup with a timestamp
ExecStart=/bin/bash -c 'rsync -av /home/john/Documents /backup/Documents-$(date +\%Y\%m\%d)'
  • We use rsync -av instead of cp for incremental backups (faster for large folders).
  • The timestamp $(date +\%Y\%m\%d) ensures unique backup folders (e.g., Documents-20240520).

Step 2: Create the Timer File

Next, create the .timer file to schedule the backup. Use backup.timer:

sudo nano /etc/systemd/system/backup.timer

Add:

[Unit]
Description=Trigger daily backup of Documents
Requires=backup.service  # Ensure the service exists

[Timer]
# Run daily at 2:30 AM
OnCalendar=*-*-* 02:30:00
# Add a random 1-5 minute delay to avoid server load
RandomizedDelaySec=1min 5min
# If the system is off at 2:30 AM, run backup on next boot
Persistent=true

[Install]
WantedBy=multi-user.target  # Enable timer on boot

Step 3: Enable and Start the Timer

To activate the timer:

  1. Reload systemd to detect the new units:

    sudo systemctl daemon-reload
  2. Enable the timer to run on boot:

    sudo systemctl enable backup.timer
  3. Start the timer immediately (no need to wait for boot):

    sudo systemctl start backup.timer

Step 4: Verify the Timer

Check if the timer is active and scheduled:

sudo systemctl list-timers --all backup.timer

Output should look like:

NEXT                         LEFT       LAST                         PASSED       UNIT          ACTIVATES
Tue 2024-05-21 02:30:00 UTC  11h left   n/a                          n/a          backup.timer  backup.service

To view logs later (after the first run):

journalctl -u backup.service  # Logs from the backup task
journalctl -u backup.timer    # Logs from the timer itself

Advanced Timer Configuration

Systemd timers support sophisticated scheduling scenarios. Let’s explore key advanced features.

1. Calendar Events (OnCalendar)

The OnCalendar directive uses a flexible syntax to define calendar-based schedules. The format is:

OnCalendar=<year>-<month>-<day> <hour>:<minute>:<second>

Wildcards (*), ranges (-), and steps (/) are supported. Examples:

ScheduleOnCalendar Syntax
Daily at 3:15 AM*-*-* 03:15:00
Every Monday at 9 PMMon *-*-* 21:00:00
1st of every month at noon*-*-01 12:00:00
Every 15 minutes*:0/15 (shorthand for *-*-* *:0/15:00)
Weekdays (Mon-Fri) at 8 AMMon,Tue,Wed,Thu,Fri *-*-* 08:00:00

Shorthand aliases like daily, weekly, or monthly also work (e.g., OnCalendar=daily = *-*-* 00:00:00).

2. Monotonic (Relative) Timers

Monotonic timers schedule tasks relative to system events (e.g., boot time or timer activation), not calendar time. Use these directives:

  • OnBootSec: Run X seconds after the system boots (e.g., OnBootSec=5min).
  • OnActiveSec: Run X seconds after the timer is activated (e.g., OnActiveSec=1h).
  • OnUnitActiveSec: Run X seconds after the service last ran (e.g., OnUnitActiveSec=6h for every 6 hours after the service finishes).

Example: Run a cleanup task 10 minutes after boot and every 2 hours thereafter:

[Timer]
OnBootSec=10min       # First run: 10min after boot
OnUnitActiveSec=2h    # Subsequent runs: 2h after the service last finished

3. Randomized Delays

To prevent multiple timers from running simultaneously (e.g., after a reboot), use RandomizedDelaySec to add a random delay. Syntax:

RandomizedDelaySec=30s  # Fixed 30-second delay
RandomizedDelaySec=1min 5min  # Random delay between 1-5 minutes

4. Persistent Tasks

If the system is offline during a scheduled run, Persistent=true ensures the task runs on the next boot. Example:

[Timer]
OnCalendar=*-*-* 01:00:00  # Daily at 1 AM
Persistent=true  # Run 1 AM backup on next boot if system was off

Managing Timers: Commands and Best Practices

Systemd provides a suite of commands to manage timers. Here are the most useful:

List All Active Timers

View upcoming and recent timer runs:

systemctl list-timers

Add --all to include inactive timers:

systemctl list-timers --all

Check Timer Status

View details about a specific timer (e.g., backup.timer):

systemctl status backup.timer

Output includes next run time, last run time, and active status.

Start/Stop/Enable/Disable Timers

  • Start a timer immediately:

    sudo systemctl start backup.timer  
  • Stop a running timer (prevents future runs until restarted):

    sudo systemctl stop backup.timer  
  • Enable a timer to run on boot:

    sudo systemctl enable backup.timer  
  • Disable a timer (stops it from running on boot):

    sudo systemctl disable backup.timer  

View Timer/Service Logs

Use journalctl to debug timers and tasks:

  • Logs for the timer itself:

    journalctl -u backup.timer  
  • Logs for the associated service (task output):

    journalctl -u backup.service  
  • Follow real-time logs:

    journalctl -u backup.service -f  

Debugging and Troubleshooting Timers

If your timer isn’t working, use these steps to diagnose issues:

1. Verify Timer and Service Files

Ensure the timer and service files are valid:

# Check for syntax errors in the timer
sudo systemctl cat backup.timer  # View the timer file
sudo systemd-analyze verify backup.timer  # Validate syntax

# Check the service file similarly
sudo systemctl cat backup.service
sudo systemd-analyze verify backup.service

2. Check if the Timer Is Enabled/Active

systemctl is-enabled backup.timer  # Should return "enabled"
systemctl is-active backup.timer   # Should return "active"

3. Inspect Logs for Errors

Use journalctl to check for failed runs or service errors:

journalctl -u backup.service --since "24 hours ago"  # Last 24 hours of service logs

Common issues:

  • ExecStart command errors (e.g., missing script, permission denied).
  • Dependencies not met (e.g., Requires=mount-backup-drive.service failed).

4. Test the Service Manually

Bypass the timer and run the service directly to validate the task:

sudo systemctl start backup.service  # Run the service now
systemctl status backup.service      # Check if it succeeded

Real-World Use Cases

Systemd timers shine in scenarios where flexibility and system integration matter. Here are common use cases:

1. Scheduled Backups with Dependencies

Use Requires=network.target and After=network.target to ensure backups run only when the internet is available (e.g., cloud backups).

2. Post-Boot Maintenance

Run tasks like disk checks or software updates 10 minutes after boot with OnBootSec=10min.

3. Periodic Log Rotation

Replace logrotate (which uses cron) with a timer to rotate logs hourly, with OnCalendar=*:00 and RandomizedDelaySec=5min.

4. Syncing Data with a Remote Server

Use OnUnitActiveSec=30min to sync files every 30 minutes after the last sync, ensuring minimal bandwidth usage.

5. Missed Task Recovery

For critical tasks like security scans, Persistent=true ensures scans run even if the system was offline during the scheduled time.

Conclusion

Systemd timers offer a modern, flexible alternative to cron for automating tasks in Linux. By combining calendar-based and relative scheduling with systemd’s dependency management and logging, they simplify complex automation workflows. Whether you’re a home user automating backups or a sysadmin managing enterprise servers, systemd timers provide the tools to keep your systems running efficiently.

Start small (e.g., the daily backup example in this blog) and gradually explore advanced features like dependencies and persistent tasks. With systemd timers, you’ll gain greater control over your system’s automation than ever before.

References