Skip to content

Cron and Task Scheduling | Linux

The cron daemon (crond) is a time-based job scheduler that runs commands at specified times and Intervals. It wakes up every minute, checks all crontab files for matching time specifications, and Executes due commands.

Terminal window
## Check if cron is running
systemctl status cron # Debian/Ubuntu
systemctl status crond # Fedora/RHEL
## Start/enable cron
systemctl enable --now cron
systemctl enable --now crond
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12)
# | | | | .---- day of week (0 - 6, 0 = Sunday)
# | | | | |
# * * * * * command
Terminal window
# Examples
* * * * * /usr/bin/command # every minute
*/5 * * * * /usr/bin/command # every 5 minutes
0 * * * * /usr/bin/command # every hour (at minute 0)
0 2 * * * /usr/bin/command # every day at 2:00 AM
0 0 * * 0 /usr/bin/command # every Sunday at midnight
0 0 1 * * /usr/bin/command # first of every month at midnight
0 0 1 1 * /usr/bin/command # January 1 at midnight
30 4 1,15 * * /usr/bin/command # 1st and 15th at 4:30 AM
0 9-17 * * 1-5 /usr/bin/command # every hour 9-17 on weekdays
0 */2 * * * /usr/bin/command # every 2 hours
15 6 * * 2-6 /usr/bin/command # 6:15 AM, Tuesday through Saturday
StringEquivalentDescription
@yearly0 0 1 1 *Once per year
@annually0 0 1 1 *Same as @yearly
@monthly0 0 1 * *Once per month
@weekly0 0 * * 0Once per week
@daily0 0 * * *Once per day
@midnight0 0 * * *Same as @daily
@hourly0 * * * *Once per hour
@reboot(special)Run once at cron daemon startup
Terminal window
# System backup every day at midnight
@daily /usr/local/bin/backup.sh
# Weekly report every Monday at 6 AM
@weekly /usr/local/bin/generate-report.sh
# Cleanup on reboot
@reboot /usr/local/bin/cleanup.sh
Terminal window
# Step values
*/15 * * * * # every 15 minutes
1-31/2 * * * * # every other day of the month (1,3,5,...,31)
# Range with step
0 6-18/2 * * * # every 2 hours from 6 AM to 6 PM (6,8,10,...,18)
# Day of week with day of month (both must match)
0 0 15 * 1 # 15th of the month AND Monday (not common)
# To specify "15th OR Monday", use two separate lines:
0 0 15 * * # 15th of every month
0 0 * * 1 # every Monday
# Lists
0 0 * * 1,3,5 # Monday, Wednesday, Friday
Terminal window
# Edit user"s crontab
crontab -e
# List user's crontab
crontab -l
# Remove user's crontab
crontab -r
# Remove with confirmation
crontab -i -r
# Edit another user's crontab (root only)
crontab -e -u username
crontab -l -u username
# Replace crontab from file
crontab crontab_file.txt
# Validate crontab syntax (some implementations)
crontab -l 2>&1 | head -1
/bin
# Crontab environment is minimal — NOT the same as interactive shell
# These are the defaults set by cron:
# SHELL=/bin/sh
# HOME=/home/username (or /root)
# LOGNAME=username
# Set environment variables in crontab
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
JAVA_HOME=/usr/lib/jvm/java-11
LANG=en_US.UTF-8
# These take effect for all subsequent cron entries
*/5 * * * * /usr/local/bin/myapp --config /etc/myapp.conf
Terminal window
# WRONG — command not found in cron's minimal PATH
* * * * * python3 /path/to/script.py
# /bin/sh: python3: command not found
# CORRECT — use full paths
* * * * * /usr/bin/python3 /path/to/script.py
# CORRECT — set PATH in crontab
PATH=/usr/local/bin:/usr/bin:/bin
* * * * * python3 /path/to/script.py
# CORRECT — source environment in the script
* * * * * /bin/bash -c 'source ~/.bashrc && python3 /path/to/script.py'
Terminal window
# Send cron output via email
MAILTO=admin@example.com
0 2 * * * /usr/local/bin/backup.sh
# Disable email for a specific job
MAILTO=""
0 * * * * /usr/local/bin/check_health.sh
# Send to multiple addresses
MAILTO="admin@example.com,oncall@example.com"
Terminal window
# /etc/cron.allow — if it exists, only listed users can use cron
# /etc/cron.deny — if it exists, listed users CANNOT use cron
#
# Priority: cron.allow > cron.deny
# If neither exists: only root can use cron (varies by distribution)
# If both exist: cron.allow takes precedence
# Create /etc/cron.allow
echo "admin" | sudo tee -a /etc/cron.allow
echo "deploy" | sudo tee -a /etc/cron.allow
# Create /etc/cron.deny
echo "nobody" | sudo tee -a /etc/cron.deny
# /etc/crontab — system-wide crontab
# Format includes a username field (unlike user crontabs)
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of system crontab entries
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
/etc/cron.d/myapp
# Drop-in directory for application cron jobs
# Files must be owned by root and not writable by group/other
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
* * * * * appuser /usr/local/bin/myapp-health-check
# /etc/cron.d/logrotate
SHELL=/bin/bash
*/15 * * * * root /usr/sbin/logrotate /etc/logrotate.conf
# Permissions on cron.d files must be 0644
chmod 644 /etc/cron.d/myapp
chown root:root /etc/cron.d/myapp

/etc/cron.hourly, cron.daily, cron.weekly, cron.monthly

Section titled “/etc/cron.hourly, cron.daily, cron.weekly, cron.monthly”
Terminal window
# Scripts in these directories are run by run-parts
# Scripts must be executable and not have dots or other special characters in filenames
# List scheduled scripts
ls -la /etc/cron.daily/
# logrotate man-db.cron ...
# Add a daily job
cat > /etc/cron.daily/my-daily-job << 'EOF'
#!/bin/bash
/usr/local/bin/daily-maintenance
EOF
chmod 755 /etc/cron.daily/my-daily-job

anacron (anachronistic cron) is designed for systems that are not running 24/7. Unlike cron, which Assumes the system is always on, anacron ensures that jobs run at the specified intervals relative To the last run, even if the system was off.

Terminal window
# anacron configuration
cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron
# See anacron(8) and anacrontab(5) for details.
#
# period delay job-identifier command
1 5 cron.daily nice run-parts --report /etc/cron.daily
7 10 cron.weekly nice run-parts --report /etc/cron.weekly
@monthly 15 cron.monthly nice run-parts --report /etc/cron.monthly
# period: number of days between runs
# delay: minutes to wait after anacron starts before running
# job-identifier: unique name (used for timestamp files in /var/spool/anacron/)
# command: the command to run
Terminal window
# Run anacron manually
anacron -n # run all jobs now (no delay)
anacron -s # run jobs synchronously
anacron -f # force jobs to run (ignore timestamps)
anacron -t # test configuration (dry run)
# Check timestamps
ls -la /var/spool/anacron/
# cron.daily cron.monthly cron.weekly
Featurecronanacron
System type24/7 serversDesktops, laptops
Missed jobsSkippedRun at next opportunity
Granularity1-minute precisionDaily minimum
Runs atExact specified timesAfter boot (with delay)
Per-userYesNo (system-wide only)
Suitable forPrecise schedulingEnsuring periodic tasks run

Systemd timers provide an alternative to cron with tighter integration into the systemd ecosystem.

/etc/systemd/system/backup.timer
[Unit]
Description=Daily backup timer
[Timer]
# Calendar expression (systemd.time calendar format)
OnCalendar=*-*-* 02:00:00
# Run immediately if a scheduled run was missed
# (e.g., system was off at 2 AM)
Persistent=true
# Randomize start time by up to 10 minutes
# (prevents thundering herd on many systems)
RandomizedDelaySec=10m
# Accuracy — timer may fire this much late (default 1 min)
AccuracySec=1min
[Install]
WantedBy=timers.target
/etc/systemd/system/backup.service
[Unit]
Description=Daily backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
Group=backup
Terminal window
# systemd calendar expressions (see systemd.time(7))
OnCalendar=*-*-* 02:00:00 # daily at 2 AM
OnCalendar=Mon *-*-* 03:00:00 # every Monday at 3 AM
OnCalendar=*-*-01 00:00:00 # first of every month
OnCalendar=Mon..Fri *-*-* 09:00:00 # weekdays at 9 AM
OnCalendar=*-*-* 00/3:00:00 # every 3 hours
OnCalendar=hourly # same as above
OnCalendar=daily # every day at midnight
OnCalendar=weekly # every Monday at midnight
OnCalendar=monthly # first of every month
OnCalendar=quarterly # every 3 months
OnCalendar=semi-annually # every 6 months
OnCalendar=yearly # every year
OnCalendar=*-*-* 02:00:00 UTC # daily at 2 AM UTC
OnCalendar=Mon,Fri *-*-* 17,18:00:00 # Mon and Fri at 5 PM and 6 PM
# Validate a calendar expression
systemd-analyze calendar '*-*-* 02:00:00'
systemd-analyze calendar 'Mon..Fri *-*-* 09:00:00'
# Show next scheduled runs
systemd-analyze calendar --iterations=5 'Mon *-*-* 03:00:00'
Terminal window
# List all timers
systemctl list-timers --all
# List timers for a specific unit
systemctl list-timers backup.timer
# Enable and start a timer
systemctl enable --now backup.timer
# Stop and disable
systemctl stop backup.timer
systemctl disable backup.timer
# Check timer status
systemctl status backup.timer
# View timer logs
journalctl -u backup.timer
journalctl -u backup.service
# Show timer calendar
systemctl show backup.timer --property=NextElapseUSecRealtime
Featurecronsystemd timer
Boot catch-upNo (missed jobs skipped)Yes (Persistent=true)
LoggingEmail or /var/log/syslogjournald
DependenciesNoneFull systemd dependency
Resource limitsSystem defaultsPer-service limits
Randomized delayNoRandomizedDelaySec
Calendar syntaxcron expressionsystemd calendar events
Per-user timersYesYes (--user)
Precision1 minute1 minute (AccuracySec)
Built-in monitoringNoYes (systemctl status)
Timezone handlingSystem timezonePer-timer Timezone=