How to Schedule Automated Tasks with cron and anacron on RHEL 7
Automation is at the heart of effective Linux system administration. Red Hat Enterprise Linux 7 provides two complementary scheduling systems: cron for tasks that need to run at precise times on continuously running servers, and anacron for tasks that must eventually run on machines that are not always powered on. Understanding both systems — along with the file locations, log output and common pitfalls — will let you schedule any repetitive task reliably. This tutorial covers the complete picture of task scheduling on RHEL 7, from simple crontab entries to system-wide job directories and the subtleties of anacron for laptops and intermittently running systems.
Prerequisites
- RHEL 7 system with root or sudo access.
- The
croniepackage installed (default on RHEL 7):yum install -y cronie. - crond and crond-anacron services running:
systemctl enable crond systemctl start crond
Step 1: Understanding crontab Syntax
A crontab entry consists of five time fields followed by the command to execute. Every field must be filled — there is no shorthand for “any” other than the asterisk (*).
# Crontab field format:
# .---------------- minute (0-59)
# | .------------- hour (0-23)
# | | .---------- day of month (1-31)
# | | | .------- month (1-12 or jan-dec)
# | | | | .---- day of week (0-7, 0 and 7 = Sunday, or sun-sat)
# | | | | |
# * * * * * command to run
Special characters extend the basic syntax:
*— Every value in the field*/5— Every 5th value (step)1,15— Value 1 and value 15 (list)1-5— Values 1 through 5 (range)1-5/2— Every 2nd value in the range 1-5 (range with step)
# Examples:
# Run at 2:30 AM every day
30 2 * * * /usr/local/bin/backup.sh
# Run every 5 minutes
*/5 * * * * /usr/local/bin/check-service.sh
# Run at 8 AM on weekdays (Monday-Friday)
0 8 * * 1-5 /usr/local/bin/send-report.sh
# Run at midnight on the 1st and 15th of every month
0 0 1,15 * * /usr/local/bin/billing-run.sh
# Run every hour between 9 AM and 5 PM on weekdays
0 9-17 * * 1-5 /usr/local/bin/hourly-sync.sh
# Run every 30 minutes during business hours on weekdays
*/30 8-18 * * 1-5 /usr/local/bin/poll.sh
Step 2: Managing Crontabs with crontab -e
User-specific crontab entries are stored in /var/spool/cron/. Never edit these files directly — always use the crontab command which validates the syntax before saving.
# Edit the current user's crontab (opens in $EDITOR, default vi)
crontab -e
# List the current user's crontab
crontab -l
# Remove the current user's crontab (asks for confirmation)
crontab -r
# Edit another user's crontab (root only)
crontab -u john -e
# List another user's crontab (root only)
crontab -u john -l
When editing with crontab -e, always include environment variables needed by your script at the top of the crontab:
# Add to the top of your crontab to set environment for all jobs:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
[email protected]
# Now the job entries:
0 2 * * * /usr/local/bin/daily-backup.sh
The MAILTO variable is important: by default, cron sends the output of every job to the user’s local mailbox. Setting it to an email address sends output there; setting it to an empty string (MAILTO="") suppresses all mail output.
Step 3: Using Special String Shortcuts
Cron on RHEL 7 supports special shorthand strings that replace the five time fields for common scheduling patterns. These are easier to read and less error-prone than constructing the equivalent five-field expression.
# Special string shortcuts:
@reboot /usr/local/bin/startup-init.sh
@hourly /usr/local/bin/hourly-check.sh
@daily /usr/local/bin/daily-report.sh # same as: 0 0 * * *
@midnight /usr/local/bin/midnight-cleanup.sh # same as @daily
@weekly /usr/local/bin/weekly-summary.sh # same as: 0 0 * * 0
@monthly /usr/local/bin/monthly-billing.sh # same as: 0 0 1 * *
@yearly /usr/local/bin/annual-archive.sh # same as: 0 0 1 1 *
@annually /usr/local/bin/annual-archive.sh # same as @yearly
The @reboot entry is particularly useful for scripts that need to run once each time the system boots, such as mounting network shares or starting custom services.
Step 4: System-Wide cron Files — /etc/cron.d/ and Drop-In Directories
Beyond per-user crontabs, RHEL 7 provides several system-wide locations for cron jobs:
- /etc/cron.d/ — Drop-in directory for individual cron files. Each file uses the same syntax as a crontab, but includes a username field before the command.
- /etc/cron.daily/ — Scripts placed here run once per day (via anacron or crond).
- /etc/cron.weekly/ — Scripts placed here run once per week.
- /etc/cron.monthly/ — Scripts placed here run once per month.
- /etc/cron.hourly/ — Scripts placed here run once per hour via crond directly.
# Create a system-wide cron job in /etc/cron.d/
# NOTE: /etc/cron.d/ files require a username field (6th field before command)
cat > /etc/cron.d/database-backup < /etc/cron.daily/logrotate-custom << 'EOF'
#!/bin/bash
/usr/sbin/logrotate /etc/logrotate.conf --state /var/lib/logrotate/logrotate.status
EOF
chmod +x /etc/cron.daily/logrotate-custom
# List what's in the drop-in directories
ls -la /etc/cron.daily/
ls -la /etc/cron.weekly/
Scripts in the drop-in directories (/etc/cron.daily/ etc.) must be executable and must not have a file extension — crond ignores files with extensions like .sh in these directories.
# WRONG — crond will ignore this file due to the .sh extension
/etc/cron.daily/my-script.sh
# CORRECT — no extension
/etc/cron.daily/my-script
Step 5: anacron for Missed Jobs
A critical weakness of standard cron is that if a job’s scheduled time passes while the machine is off or asleep, the job simply does not run. This is where anacron comes in. It is designed for systems that are not running continuously, ensuring that periodic jobs (daily, weekly, monthly) eventually run even if the system was down at their scheduled time.
On RHEL 7, anacron is integrated into crond. The crond daemon runs anacron internally to handle the /etc/cron.daily/, /etc/cron.weekly/ and /etc/cron.monthly/ directories.
# View the anacron configuration
cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron
# See anacron(8) and anacrontab(5) for details.
# SHELL=/bin/sh
# PATH=/sbin:/bin:/usr/sbin:/usr/bin
# MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22
# period in days, delay in minutes, 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
The fields in /etc/anacrontab are:
- Period — How many days between runs (1=daily, 7=weekly, @monthly=monthly).
- Delay — Minutes to wait after boot before running this job (staggers jobs to avoid boot-time load spikes).
- Job-ID — A unique identifier used in timestamp files under
/var/spool/anacron/. - Command — The command to run.
# anacron records the last run date in timestamp files
ls -la /var/spool/anacron/
# -rw------- 1 root root 9 May 17 03:05 cron.daily
# -rw------- 1 root root 9 May 10 03:25 cron.weekly
# -rw------- 1 root root 9 May 1 03:45 cron.monthly
# Manually trigger anacron to run all overdue jobs immediately (testing)
anacron -fn
# Run anacron for a specific job only
anacron -f cron.daily
# Check when jobs last ran
cat /var/spool/anacron/cron.daily
Step 6: Viewing cron Logs
On RHEL 7, cron activity is logged to /var/log/cron. This is the first place to look when a job does not appear to be running.
# View cron log
tail -50 /var/log/cron
# Watch cron log in real time
tail -f /var/log/cron
# Filter for a specific user's jobs
grep "john" /var/log/cron
# Filter for failed or error entries
grep -i "error|fail|MAIL" /var/log/cron
# Check that crond itself is running and healthy
systemctl status crond
journalctl -u crond --since today
A common debugging technique is to redirect a job’s output to a log file within the crontab entry itself:
# Redirect stdout and stderr to a log file for debugging
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup-cron.log 2>&1
# Suppress all output (use when the job is confirmed working)
0 2 * * * /usr/local/bin/backup.sh > /dev/null 2>&1
Conclusion
Cron and anacron together provide complete task automation coverage on RHEL 7. Use per-user crontab -e for user-specific jobs, /etc/cron.d/ for system-wide jobs that need precise scheduling, and the drop-in directories (/etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/) for jobs that just need to run periodically. Always remember that scripts in drop-in directories must be executable and have no file extension. Set MAILTO appropriately, redirect output to log files during development, and check /var/log/cron when jobs do not run as expected. With anacron handling missed jobs, your periodic maintenance tasks will run reliably even on systems with unpredictable uptime.