Linux crontab is the built-in job scheduler on every Linux distribution and macOS. Run crontab -e to open your schedule, add a cron expression followed by a command, save, and the system handles the rest. Here is the complete setup, real examples, and fixes for every common problem.
| Command | What It Does |
|---|---|
| crontab -e | Edit your personal crontab (opens in default editor) |
| crontab -l | List all your current cron jobs |
| crontab -r | Remove your entire crontab (use carefully — deletes ALL jobs) |
| crontab -r -i | Remove with confirmation prompt (safer) |
| sudo crontab -e | Edit the root user crontab |
| sudo crontab -l -u www-data | List cron jobs for a specific user (requires root) |
crontab -e — choose nano if prompted for an editor (easiest for beginners)*/5 * * * * echo "cron works" >> /tmp/crontest.log 2>&1cat /tmp/crontest.logcrontab -e and delete the line| Cron Expression | Command | Purpose |
|---|---|---|
| 0 2 * * * | /home/user/scripts/backup.sh >> /var/log/backup.log 2>&1 | Nightly backup at 2 AM |
| */5 * * * * | /usr/bin/python3 /home/user/healthcheck.py | Health check every 5 min |
| 0 9 * * 1-5 | /home/user/scripts/report.sh | mail -s "Daily Report" [email protected] | Weekday 9 AM report |
| 0 0 * * 0 | /usr/sbin/logrotate /etc/logrotate.conf | Weekly log rotation |
| 0 */6 * * * | /usr/bin/certbot renew --quiet | SSL cert renewal check every 6 hrs |
| 30 4 1 * * | /home/user/scripts/monthly-cleanup.sh > /dev/null 2>&1 | Monthly cleanup, no output |
Linux has two ways to schedule cron jobs: user crontabs and system cron directories. Both are active simultaneously.
| Location | Managed By | Use Case | Includes Username Field |
|---|---|---|---|
| crontab -e | Individual users | User-specific scheduled tasks | No (runs as that user) |
| /etc/crontab | System admin | System-wide jobs with user specification | Yes (root, www-data, etc.) |
| /etc/cron.d/ | Package managers, admin | Individual cron files per service | Yes |
| /etc/cron.hourly/ | System (run-parts) | Scripts that run every hour | N/A (just executable scripts) |
| /etc/cron.daily/ | System (run-parts) | Scripts that run daily (~6:25 AM) | N/A |
| /etc/cron.weekly/ | System (run-parts) | Scripts that run weekly (Sunday ~6:47 AM) | N/A |
| /etc/cron.monthly/ | System (run-parts) | Scripts that run monthly (1st ~6:52 AM) | N/A |
The /etc/cron.daily/ and similar directories are the simplest option for interval-based jobs — just drop an executable script into the directory. No cron expression needed. The system scheduler (anacron) handles the timing.
Cron runs with a minimal environment — typically PATH=/usr/bin:/bin. This means commands that work in your terminal may fail in cron because cron cannot find them.
Fix: always use full paths in cron jobs.
python3 script.py, use /usr/bin/python3 /full/path/to/script.pynode app.js, use /usr/local/bin/node /full/path/to/app.jswhich python3 or which nodeAlternatively, set PATH at the top of your crontab:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin as the first line in crontab -e| Problem | Diagnosis | Fix |
|---|---|---|
| Job not running at all | Check if cron service is active: systemctl status cron | sudo systemctl start cron && sudo systemctl enable cron |
| Command not found errors | PATH issue — cron has minimal PATH | Use full paths: /usr/bin/python3 instead of python3 |
| Permission denied | Script not executable | chmod +x /path/to/script.sh |
| No output or error messages | Output goes to mail or nowhere | Add >> /tmp/job.log 2>&1 to capture output |
| Job runs but produces wrong results | Environment variables missing | Source your env in the script: source /home/user/.env |
| Job runs at wrong time | Timezone mismatch | Check with timedatectl — cron uses system timezone |
| Job seems to run multiple times | Overlapping executions | Add flock lock: flock -n /tmp/job.lock /path/to/script.sh |
| Cron email flooding inbox | Every job output goes to user mail | Add > /dev/null 2>&1 or set MAILTO="" in crontab |
grep CRON /var/log/syslogcat /var/log/crontail -f /var/log/syslog | grep CRONjournalctl -u cron --since "1 hour ago"Cron logs show when each job started but NOT the output. To capture job output, redirect it in the cron line itself: 0 9 * * * /path/to/job.sh >> /var/log/myjob.log 2>&1
Build cron expressions visually instead of hand-writing them. Verify before deploying.
Open Cron GeneratormacOS supports crontab identically to Linux. However, Apple officially recommends launchd as the preferred scheduler:
| Feature | crontab | launchd (plist) |
|---|---|---|
| Setup complexity | ✓ Simple — one-line cron expression | ~Complex — XML plist files |
| Time-based scheduling | ✓ Yes — standard cron syntax | ✓ Yes — StartCalendarInterval |
| Run after sleep/wake | ✗ Missed jobs are skipped | ✓ Catches up missed jobs |
| Event-based triggers | ✗ Time only | ✓ File changes, network events, login |
| Logging | ~Manual redirect | ✓ Built-in stdout/stderr paths |
| Process management | ✗ No restart on failure | ✓ KeepAlive, restart policies |
| Best for | ✓ Simple scheduled scripts | ~Complex daemons and agents |
For simple scheduled tasks (backups, scripts, health checks), crontab on macOS is perfectly fine. Use launchd when you need event-driven triggers or want the system to catch up on missed jobs after sleep.
Cron is excellent for simple, time-based scheduling on a single machine. You will outgrow it when you need:
Generate valid cron expressions in seconds — no syntax errors, no guessing.
Open Cron Generator