Scheduling Recurring Tasks for Daemons and OS Functions
College-Level Course Module | RHEL System Administration
Learning Objectives
1
Understand systemd timers
Create and manage timer units for scheduled service activation
2
Configure system cron jobs
Use /etc/crontab, /etc/cron.d/, and cron directories for system tasks
3
Use anacron for resilient scheduling
Ensure jobs run on systems that aren't continuously powered
4
Manage temporary files with systemd-tmpfiles
Configure automatic cleanup of temporary and transient files
System Scheduling Overview
System scheduling runs recurring tasks that maintain OS health: log rotation, temporary file cleanup, security updates, database maintenance, and service health checks.
systemd Timers
Modern approach. Timer unit activates a service unit on schedule. Full systemd integration.
System Cron
/etc/crontab and /etc/cron.d/. Traditional scheduling with username field.
anacron
Catches up on missed jobs. Used for daily/weekly/monthly system tasks.
systemd-tmpfiles
Manages temporary files. Creates, cleans, and removes files/directories.
Root Required: System scheduling mechanisms require administrator privileges to configure.
systemd Timers Introduction
A systemd timer is a unit that activates another unit (usually a service) based on time. Timers are the systemd replacement for cron jobs.
# List all timers[root@server ~]# systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sun 2024-01-21 00:00:00 EST 5h 30min left Sat 2024-01-20 00:00:00 EST 18h ago logrotate.timer logrotate.service
Sun 2024-01-21 00:00:00 EST 5h 30min left Sat 2024-01-20 00:00:00 EST 18h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2024-01-22 00:00:00 EST 1 day left Mon 2024-01-15 00:00:00 EST 5 days ago fstrim.timer fstrim.service# Include inactive timers[root@server ~]# systemctl list-timers --all
# Show timer status[root@server ~]# systemctl status logrotate.timer
● logrotate.timer - Daily rotation of log files
Loaded: loaded (/usr/lib/systemd/system/logrotate.timer; enabled)
Active: active (waiting)
The OnCalendar directive uses a flexible calendar event syntax: DayOfWeek Year-Month-Day Hour:Minute:Second
*-*-* 02:00:00
Every day at 2:00 AM
Mon *-*-* 09:00:00
Every Monday at 9:00 AM
*-*-01 00:00:00
First of every month at midnight
*-01-01 00:00:00
January 1st each year
Mon..Fri *-*-* 08:00:00
Weekdays at 8:00 AM
*-*-* *:00,30:00
Every 30 minutes
daily
Shorthand for *-*-* 00:00:00
weekly
Shorthand for Mon *-*-* 00:00:00
# Test calendar expressions with systemd-analyze[root@server ~]# systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"
Original form: Mon..Fri *-*-* 09:00:00
Normalized form: Mon..Fri *-*-* 09:00:00
Next elapse: Mon 2024-01-22 09:00:00 EST
(in UTC): Mon 2024-01-22 14:00:00 UTC
From now: 1 day 3h left
Monotonic Timers
Monotonic timers trigger relative to a reference point (boot time, unit activation, etc.) rather than wall-clock time.
Directive
Triggers Relative To
Example
OnBootSec=
System boot
OnBootSec=5min
OnStartupSec=
systemd start (similar to boot)
OnStartupSec=10min
OnActiveSec=
Timer activation
OnActiveSec=1h
OnUnitActiveSec=
Last activation of triggered unit
OnUnitActiveSec=30min
OnUnitInactiveSec=
Last deactivation of triggered unit
OnUnitInactiveSec=1h
# Timer that runs 5 minutes after boot, then every hour[Timer]OnBootSec=5minOnUnitActiveSec=1h# Timer that runs 15 minutes after boot only[Timer]OnBootSec=15min
Timer Options
[Timer]# When to runOnCalendar=*-*-* 02:00:00# Run if missed while system was offPersistent=true# Add randomized delay (avoid thundering herd)RandomizedDelaySec=1h# Accuracy (wake from sleep if needed)AccuracySec=1min# Activate different unit than defaultUnit=my-special.service
Persistent=true: If the system was off when the timer should have fired, run the job when the system starts. Critical for maintenance tasks!
RandomizedDelaySec: Prevents all systems from running the same job simultaneously. Important for distributed environments.
Creating a Custom Timer
# Create the service unit[root@server ~]# vim /etc/systemd/system/cleanup.service
# List all timers (active and inactive)[root@server ~]# systemctl list-timers --all
# Check timer status[root@server ~]# systemctl status cleanup.timer
# Enable timer to start at boot[root@server ~]# systemctl enable cleanup.timer
# Start timer now[root@server ~]# systemctl start cleanup.timer
# Stop timer[root@server ~]# systemctl stop cleanup.timer
# Disable timer (won't start at boot)[root@server ~]# systemctl disable cleanup.timer
# Manually trigger the associated service[root@server ~]# systemctl start cleanup.service
# View logs from timer-triggered service[root@server ~]# journalctl -u cleanup.service
# View recent timer activity[root@server ~]# journalctl -u cleanup.timer --since "1 week ago"
System Crontab
The system crontab /etc/crontab has an extra field specifying which user runs each job. Only root can edit this file.
[root@server ~]# cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue...
# | | | | |
# * * * * * user-name command to be executed
# MIN HOUR DOM MON DOW USER COMMAND
0 3 * * * root /usr/local/bin/backup.sh
30 4 * * 0 root /usr/local/bin/weekly-maintenance.sh
Note: The username field between day-of-week and command is what distinguishes /etc/crontab from user crontabs.
The /etc/cron.d/ Directory
Drop-in crontab files in /etc/cron.d/ use the same format as /etc/crontab (with username field). Packages install their cron jobs here.
# List cron.d contents[root@server ~]# ls -la /etc/cron.d/
-rw-r--r--. 1 root root 128 Jan 10 10:00 0hourly
-rw-r--r--. 1 root root 108 Jan 10 10:00 raid-check
-rw-r--r--. 1 root root 121 Jan 10 10:00 sysstat# View a cron.d file[root@server ~]# cat /etc/cron.d/sysstat
# Run system activity accounting tool every 10 minutes
*/10 * * * * root /usr/lib64/sa/sa1 1 1 -S DISK
# Generate daily summary at 23:53
53 23 * * * root /usr/lib64/sa/sa2 -A# Create a custom cron.d file[root@server ~]# vim /etc/cron.d/myapp
# MyApp maintenance
0 2 * * * root /opt/myapp/bin/maintenance.sh
Advantages: Each application can have its own file. Removing the package removes its cron file. Easier to manage than one big crontab.
Cron Directories
/etc/cron.hourly/ ← Scripts run every hour /etc/cron.daily/ ← Scripts run daily /etc/cron.weekly/ ← Scripts run weekly /etc/cron.monthly/ ← Scripts run monthly
# List daily cron scripts[root@server ~]# ls -la /etc/cron.daily/
-rwxr-xr-x. 1 root root 219 Jan 10 logrotate
-rwxr-xr-x. 1 root root 618 Jan 10 man-db
-rwxr-xr-x. 1 root root 608 Jan 10 mlocate# Add a custom daily script[root@server ~]# vim /etc/cron.daily/custom-cleanup
[root@server ~]# chmod +x /etc/cron.daily/custom-cleanup
# Scripts must be executable![root@server ~]# ls -l /etc/cron.daily/custom-cleanup
-rwxr-xr-x. 1 root root 156 Jan 20 15:00 custom-cleanup
Important: Scripts must be executable (chmod +x) and should not have file extensions like .sh (run-parts skips files with dots).
anacron
anacron runs jobs that were scheduled but missed because the system was off. It ensures daily, weekly, and monthly jobs eventually run.
[root@server ~]# cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
RANDOM_DELAY=45
START_HOURS_RANGE=3-22
#period delay job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly# Check when jobs last ran[root@server ~]# ls -la /var/spool/anacron/
-rw-------. 1 root root 9 Jan 20 03:15 cron.daily
-rw-------. 1 root root 9 Jan 14 03:30 cron.weekly
-rw-------. 1 root root 9 Jan 1 03:45 cron.monthly
systemd-tmpfiles
systemd-tmpfiles creates, deletes, and cleans up volatile and temporary files and directories based on configuration files.
# Run tmpfiles cleanup manually[root@server ~]# systemd-tmpfiles --clean
# Create configured files/directories[root@server ~]# systemd-tmpfiles --create
# View the cleanup timer[root@server ~]# systemctl status systemd-tmpfiles-clean.timer
● systemd-tmpfiles-clean.timer - Daily Cleanup of Temporary Directories
Active: active (waiting)
Trigger: Sun 2024-01-21 00:00:00 EST; 5h left# Configuration file locations
/etc/tmpfiles.d/*.conf # Local admin configuration
/run/tmpfiles.d/*.conf # Runtime configuration
/usr/lib/tmpfiles.d/*.conf # Package defaults
tmpfiles.d Configuration
# Format: Type Path Mode User Group Age Argument# Create directory with permissions
d /run/myapp 0755 myapp myapp -
# Create directory, clean files older than 10 days
D /var/tmp/myapp 0755 root root 10d
# Create empty file
f /var/log/myapp.log 0644 myapp myapp -
# Create symlink
L /var/run/myapp - - - - /run/myapp
# Remove path recursively
R /tmp/oldcache - - - - -
# Clean files older than 30 days (Age field)
e /var/cache/myapp - - - 30d
# Create custom tmpfiles configuration[root@server ~]# vim /etc/tmpfiles.d/myapp.conf
# MyApp tmpfiles configuration
d /run/myapp 0755 myapp myapp -
f /run/myapp/app.pid 0644 myapp myapp -
D /var/tmp/myapp 0750 myapp myapp 7d# Test the configuration[root@server ~]# systemd-tmpfiles --create /etc/tmpfiles.d/myapp.conf
Comparing Methods
Feature
systemd Timers
cron/anacron
Dependency handling
Full systemd dependencies
None
Logging
journald integration
Mail or manual redirect
Missed job handling
Persistent=true
anacron
Random delay
RandomizedDelaySec
RANDOM_DELAY in anacrontab
Resource control
Full cgroup support
Limited (nice)
Calendar syntax
OnCalendar (readable)
5-field format
Boot-relative
OnBootSec, OnStartupSec
@reboot only
Testing
systemd-analyze calendar
Limited
Status visibility
systemctl status
No built-in
Familiarity
Newer, learning curve
Traditional, well-known
Recommendation: Use systemd timers for new system services. Use cron for simpler tasks or when compatibility with non-systemd systems matters.
Common System Timers
[root@server ~]# systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sun 2024-01-21 00:00:00 EST 4h left Sat 2024-01-20 00:00:00 EST 19h ago logrotate.timer logrotate.service
Sun 2024-01-21 00:00:00 EST 4h left Sat 2024-01-20 00:00:00 EST 19h ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Mon 2024-01-22 00:00:00 EST 1 day left Mon 2024-01-15 00:00:00 EST 5 days fstrim.timer fstrim.service
Sun 2024-01-21 06:00:00 EST 10h left Sat 2024-01-20 06:12:00 EST 13h ago dnf-makecache.timer dnf-makecache.service
Timer
Purpose
Default Schedule
logrotate.timer
Rotate log files to prevent disk fill
Daily
systemd-tmpfiles-clean.timer
Clean temporary directories
Daily
fstrim.timer
Discard unused SSD blocks
Weekly
dnf-makecache.timer
Refresh package metadata
Hourly (with conditions)
unbound-anchor.timer
Update DNSSEC root anchor
Monthly
Troubleshooting
# Check if timers/cron daemon running[root@server ~]# systemctl status crond
[root@server ~]# systemctl list-timers --all
# View systemd timer logs[root@server ~]# journalctl -u backup.timer
[root@server ~]# journalctl -u backup.service --since "1 day ago"
# View cron logs[root@server ~]# grep CRON /var/log/cron
[root@server ~]# journalctl -u crond --since "1 hour ago"
# Test timer calendar expression[root@server ~]# systemd-analyze calendar "Mon *-*-* 09:00:00"
# Manually run a timer's service[root@server ~]# systemctl start backup.service
# Verify cron.d file permissions[root@server ~]# ls -la /etc/cron.d/
# Files must be owned by root, not group/world writable# Check cron directory scripts are executable[root@server ~]# ls -la /etc/cron.daily/
Best Practices
✓ Do
Use systemd timers for new services
Set Persistent=true for maintenance jobs
Use RandomizedDelaySec in clusters
Log all job output to journald or files
Test scripts manually before scheduling
Use /etc/cron.d/ over /etc/crontab
Monitor job success/failure
Document what each job does
✗ Don't
Schedule intensive jobs during peak hours
Forget dependencies (network, mounts)
Use .sh extension in cron directories
Ignore failed job notifications
Hardcode paths without testing
Schedule conflicting jobs at same time
Forget to enable timers after creating
Assume the environment is your shell
Security: System scheduled jobs run as root by default. Use dedicated service accounts when possible. Validate all inputs.
Key Takeaways
1
systemd Timers: Modern scheduling with .timer + .service units. OnCalendar syntax, Persistent=true, journald logging
2
System Cron: /etc/crontab and /etc/cron.d/ with username field. Cron directories for simple daily/weekly/monthly jobs
3
anacron: Catches missed jobs on non-24/7 systems. Runs overdue daily/weekly/monthly jobs at startup
4
systemd-tmpfiles: Creates and cleans temporary files. Configure in /etc/tmpfiles.d/*.conf