Chapter Objective: Control who may schedule jobs, configure system-wide cron and systemd timer units for administrator-managed automation, and monitor whether scheduled tasks actually succeeded.
Key files/commands: /etc/cron.allow, /etc/cron.d/, OnCalendar=, logrotate
User Tasks vs. System Tasks
The previous chapter covered scheduling jobs as an individual user with crontab -e and at. This chapter shifts to the administrator's perspective: managing scheduling infrastructure for the whole system — deciding who's allowed to schedule jobs at all, and configuring automation that isn't tied to any one user's account.
Controlling Who Can Schedule Jobs
By default, most users can schedule their own cron and at jobs. Administrators can restrict this with allow/deny files.
| File | Effect |
|---|---|
/etc/cron.allow | If present, ONLY listed users may use cron — everyone else is blocked |
/etc/cron.deny | If cron.allow doesn't exist, listed users are blocked; everyone else is allowed |
/etc/at.allow | Same logic as cron.allow, but for at |
/etc/at.deny | Same logic as cron.deny, but for at |
# Only sarah and miguel may use cron; everyone else is denied
echo -e "sarah\nmiguel" | sudo tee /etc/cron.allow
# Block a specific user from using cron, allow everyone else
echo "guestuser" | sudo tee -a /etc/cron.deny
cron.allow exists at all, cron.deny is ignored completely — access is restricted to exactly the users listed in cron.allow, regardless of what's in the deny file. Don't create an allow file expecting the deny file to still add exceptions.
System Cron Directories in Depth
# /etc/crontab format includes a USER field the per-user crontab doesn't have
# minute hour day month weekday user command
0 3 * * * root /usr/local/bin/full-backup.sh
| Location | Typical Use |
|---|---|
/etc/crontab | System jobs edited directly by the administrator |
/etc/cron.d/*.conf | Packages drop their own scheduled jobs here, separate from the main file |
/etc/cron.daily/, .hourly/, .weekly/, .monthly/ | Executable scripts, run as a batch by run-parts at the matching interval |
# Drop a script into the daily directory to run it once a day
sudo cp cleanup-tmp.sh /etc/cron.daily/
sudo chmod +x /etc/cron.daily/cleanup-tmp.sh
/etc/cron.d/ exists so a package's installer can drop in its own scheduled job without needing to parse and modify the shared /etc/crontab file — cleaner for both upgrades and uninstalls.
Writing a Custom systemd Timer
A systemd timer is a pair of unit files: a .service defining what to run, and a .timer defining when.
# /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup job
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=Run backup.service nightly
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
| Directive | Meaning |
|---|---|
OnCalendar= | A calendar-style schedule, similar in spirit to cron but more expressive |
Persistent=true | If the system was off when the timer should have fired, run it as soon as possible after boot |
RandomizedDelaySec= | Adds a random delay up to N seconds, to avoid many timers firing at the exact same instant |
OnBootSec= | Alternative to OnCalendar — fire a set time after boot instead of at a wall-clock time |
# After creating both files, reload systemd's unit definitions
sudo systemctl daemon-reload
# Enable and start the TIMER, not the service directly
sudo systemctl enable --now backup.timer
# Confirm it's scheduled and see the next run time
systemctl list-timers backup.timer
systemctl enable backup.service by mistake just makes the service start at boot — it won't schedule anything. The .timer unit is what needs to be enabled for scheduling to actually happen.
logrotate: A Real System Task
logrotate is a practical, concrete example of scheduled system automation already running on every RHEL system — it periodically rotates, compresses, and eventually discards old log files so /var/log doesn't grow without bound.
# Global logrotate configuration
cat /etc/logrotate.conf
# Per-application configuration, one file per service
ls /etc/logrotate.d/
# Example snippet for a hypothetical application log
/var/log/myapp/*.log {
weekly
rotate 4
compress
missingok
notifempty
}
# Manually trigger a run (useful for testing a config)
sudo logrotate -f /etc/logrotate.conf
logrotate.timer) rather than a classic cron.daily script — a good real-world example of the two scheduling mechanisms working together.
Monitoring Scheduled Job Results
A scheduled job that fails silently is worse than one that fails loudly — always plan for how you'll know something went wrong.
# Cron mails job output to the owning user by default, if mail is configured
mail
# Check systemd timer / service run history and output
journalctl -u backup.service
# Confirm the last and next run times for a timer
systemctl list-timers --all
>> /var/log/myjob.log 2>&1) so you have somewhere reliable to check.
Key Terms for Chapter 4
- cron.allow / cron.deny
- Files controlling which users may schedule cron jobs
- run-parts
- Utility that executes every script in a directory, used by the cron.daily/weekly/monthly mechanism
- OnCalendar
- The systemd timer directive specifying a calendar-based schedule
- Persistent
- Timer directive ensuring a missed run still fires after the system comes back up
- RandomizedDelaySec
- Timer directive adding a random delay to avoid many jobs firing simultaneously
- logrotate
- Utility that rotates, compresses, and eventually removes old log files on a schedule
Review Questions
- If both
/etc/cron.allowand/etc/cron.denyexist, which one takes effect? - What extra field does
/etc/crontabhave that a per-user crontab (edited withcrontab -e) does not? - What is the purpose of
/etc/cron.d/, and why might a package prefer it over editing/etc/crontabdirectly? - When setting up a systemd timer, what unit do you actually enable — the
.serviceor the.timer? - What does
Persistent=truedo for a systemd timer? - What real, already-running system task rotates and compresses log files on a schedule?
- Why is it good practice to explicitly redirect a cron job's output to a log file?