Cron Jobs: Scheduling Tasks on Linux
Cron is the standard task scheduler on Unix-like systems. It runs commands at specified times, making it essential for backups, maintenance, data processing, monitoring, and report generation. This guide covers everything from basic usage to advanced patterns.
Crontab Syntax
Each line in a crontab file defines a scheduled command:
┌───────── minute (0-59)
│ ┌────────── hour (0-23)
│ │ ┌─────────── day of month (1-31)
│ │ │ ┌──────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-7, 0=Sun, 7=Sun)
│ │ │ │ │
* * * * * command_to_execute# Every minute
* * * * * /usr/bin/check-status.sh
# Every day at 3:30 AM
30 3 * * * /usr/bin/backup.sh
# Every weekday at 9 AM
0 9 * * 1-5 /usr/bin/daily-report.sh
# First day of every month at midnight
0 0 1 * * /usr/bin/monthly-cleanup.sh
# Every 15 minutes
*/15 * * * * /usr/bin/check-alerts.sh
# Every 2 hours during business hours
0 9-17/2 * * 1-5 /usr/bin/process-orders.shSpecial Time Strings
@reboot # Run once at startup
@yearly # 0 0 1 1 * (January 1st)
@annually # same as @yearly
@monthly # 0 0 1 * *
@weekly # 0 0 * * 0
@daily # 0 0 * * *
@hourly # 0 * * * *# Run a script 5 minutes after each reboot
@reboot sleep 5 && /usr/local/bin/start-services.sh
# Weekly security scan
@weekly /usr/local/bin/security-scan.shManaging Crontabs
# Edit your crontab
crontab -e
# List your crontab entries
crontab -l
# Remove your crontab
crontab -r
# Edit another user's crontab (root only)
sudo crontab -u www-data -e
# View another user's crontab
sudo crontab -u www-data -l
# Load crontab from file
crontab /path/to/my-crontab.txtWhen you run crontab -e, the system opens the default editor (vim, nano, or configured via EDITOR environment variable). The file is validated when you save — syntax errors prevent installation.
Crontab File Locations
- System crontab:
/etc/crontab(includes user field) - User crontabs:
/var/spool/cron/crontabs/(on Debian/Ubuntu) or/var/spool/cron/(on RHEL/CentOS) - Cron directories:
/etc/cron.hourly/,/etc/cron.daily/,/etc/cron.weekly/,/etc/cron.monthly/ - Snippet directory:
/etc/cron.d/(package-managed cron jobs)
# Adding scripts to cron directories (simpler than editing crontab)
sudo cp backup.sh /etc/cron.daily/backup
sudo chmod +x /etc/cron.daily/backup
# System crontab (includes user field)
# /etc/crontab
0 2 * * * root /usr/local/bin/system-maintenance.shLogging and Output
By default, cron sends stdout and stderr via email to the crontab owner. For scripts that produce output, redirect it explicitly:
# Discard all output
0 3 * * * /usr/bin/backup.sh > /dev/null 2>&1
# Log to file (append)
0 3 * * * /usr/bin/backup.sh >> /var/log/backup.log 2>&1
# Log with timestamp
0 3 * * * echo "[$(date)] Starting backup" >> /var/log/backup.log && /usr/bin/backup.sh >> /var/log/backup.log 2>&1
# Log to syslog
0 3 * * * /usr/bin/backup.sh | logger -t backupReading Cron Logs
# Debian/Ubuntu — cron logs to syslog
grep CRON /var/log/syslog
# RHEL/CentOS — dedicated cron log
tail -f /var/log/cron
# Watch cron activity in real time
journalctl -f -u cron
# Recent cron executions
journalctl -u cron --since "1 hour ago"Environment Variables in Crontab
Cron runs with a minimal environment. PATH is usually /usr/bin:/bin. Set variables at the top of your crontab:
# Crontab environment settings
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=admin@example.com # where to send output
HOME=/home/username
LOGNAME=username
# Use absolute paths in commands!
0 3 * * * /usr/local/bin/myscript.shA common failure is that a script works when run manually but fails under cron because of a different PATH or missing environment variables. Always test with the cron environment:
# Simulate cron environment
env -i HOME=$HOME PATH=/usr/bin:/bin SHELL=/bin/bash /usr/bin/bash -l /path/to/script.shCommon Use Cases
Database Backups
# Daily database backup at 2 AM, keep 7 days
0 2 * * * pg_dump -U postgres mydb > /backups/db-$(date +\%Y\%m\%d).sql && find /backups -name "db-*.sql" -mtime +7 -delete
# Note: % in cron must be escaped with backslashSystem Maintenance
# Clean apt cache on Sundays
0 4 * * 0 apt-get update && apt-get upgrade -y && apt-get autoremove -y
# Rotate logs
30 3 * * * /usr/sbin/logrotate /etc/logrotate.conf
# Check disk space
0 8 * * * df -h | mail -s "Disk Usage Report" admin@example.comMonitoring
# Check service health every 5 minutes
*/5 * * * * /usr/local/bin/check-services.sh
# Ping external host (network monitoring)
*/10 * * * * ping -c 1 google.com > /dev/null 2>&1 || logger -t ping "Google unreachable"
# SSL certificate expiry check
0 9 * * 1 /usr/local/bin/check-ssl.sh example.comTroubleshooting
# Check if cron daemon is running
systemctl status cron
# or
ps aux | grep cron
# Verify crontab syntax
crontab -l | crontab - # re-installs — fails if syntax error
# Test the command manually in cron-like environment
env -i HOME=$HOME PATH=/usr/bin:/bin bash -c 'command'
# Check for mail (cron sends output there)
mail
# Run as the script's owner
sudo -u username command
# Add logging
* * * * * /bin/bash -c 'echo "Running at $(date)" && /path/to/command' >> /tmp/cron-debug.log 2>&1Common Issues
- Script works manually but not in cron — PATH or environment variable mismatch
- No output — stdout/stderr not redirected; check mail or add logging
- Permission denied — script not executable; cron ignores non-executable files
- % not escaped — in crontab,
%must be\%unless it is the command delimiter - Newlines not ending with newline — crontab must end with a blank line
Security
# Allow/deny specific users
/etc/cron.allow # users listed can use cron
/etc/cron.deny # users listed cannot use cron
# If cron.allow exists, only listed users can use cron
# If only cron.deny exists, all users except listed can use cron
# Restrict cron to non-root users for compliance
echo "username" >> /etc/cron.allowReal-World Implementation Tips
Production Considerations
When moving from development to production, several factors become critical. Error handling should be comprehensive — every external call (database, API, file system) should have proper error checking, logging, and retry logic where appropriate. Performance monitoring through metrics and structured logging helps identify bottlenecks before they affect users.
Testing Strategy
A thorough testing approach combines multiple levels:
- Unit tests verify individual functions and methods in isolation
- Integration tests validate that components work together correctly
- Edge case tests cover boundary conditions, empty inputs, and error states
- Performance tests ensure the system meets latency and throughput requirements
Test data should be realistic but controlled. Mock external dependencies to make tests fast and deterministic. Aim for tests that are independent, repeatable, and fast enough to run on every commit.
Documentation
Good documentation is essential for maintainable code. Follow these principles:
- Document the “why” not just the “what” — explain design decisions
- Keep examples up to date with the code
- Include usage examples for public APIs
- Document configuration options and their defaults
- Explain error conditions and recovery strategies
Security Best Practices
Security should be considered throughout development:
- Validate all inputs at system boundaries
- Use parameterized queries for database access
- Store secrets in environment variables or secret managers
- Keep dependencies updated to patch vulnerabilities
- Apply the principle of least privilege
Performance Optimization
Optimize based on measured data, not assumptions:
- Profile before optimizing — identify actual bottlenecks
- Measure the impact of each change
- Consider the trade-off between speed and readability
- Cache expensive operations with appropriate invalidation
- Use connection pooling for database and network resources
Monitoring and Observability
Production systems need visibility:
- Structured logging with correlation IDs for request tracking
- Metrics for latency, throughput, error rates, and resource usage
- Health check endpoints for load balancers and orchestration
- Distributed tracing for request flows across services
- Alerts for anomaly detection based on baselines
These patterns apply across all programming languages and frameworks. The specific implementation varies, but the principles remain consistent.
FAQ
Q: What is the difference between crontab -e and editing /etc/crontab?
A: crontab -e edits the user’s personal crontab. /etc/crontab is the system-wide crontab that can run commands as any user and uses a different format (includes a user field).
Q: How do I run a cron job every 30 seconds?
A: Cron’s minimum interval is 1 minute. For sub-minute intervals, use a loop with sleep: * * * * * for i in 0 30; do sleep $i && command; done
Q: Why did my cron job not run?
A: Check: is cron running? Is the script executable? Are paths absolute? Is the crontab syntax correct? Check logs in /var/log/syslog or /var/log/cron.
Q: How do I prevent duplicate cron job execution?
A: Use a lock file: * * * * * /usr/bin/flock -n /tmp/mylock.lock /path/to/command
Q: Can cron handle jobs that take longer than the interval? A: Yes, but overlapping executions can cause problems. Use flock or a PID file to prevent running if the previous instance is still running.
Q: How do I set up a cron job that runs on specific days of the month?
A: Use the day-of-month field: 0 9 1,15 * * /path/to/command (runs on the 1st and 15th). Or use date tests inside the script.
For a comprehensive overview, read our article on Bash Scripting Basics.
For a comprehensive overview, read our article on Grep Command Guide.