Scheduling tasks with cron

Run things automatically on a schedule.

Beginner12 min · lesson 13 of 25

cron is the classic Linux scheduler — it runs commands automatically at times you specify, from "every minute" to "3am on the first of the month." It is how routine work gets done without a human: backups, log rotation, cleanup jobs, health checks. You describe the schedule and the command in a crontab (cron table), and cron runs it faithfully in the background.

The five-field schedule

A cron schedule is five fields — minute, hour, day-of-month, month, day-of-week — followed by the command. A * means "every" for that field. So 0 3 * * * means "at minute 0 of hour 3, every day" (3am daily), and */15 * * * * means "every 15 minutes." Reading the five fields left to right is the whole skill; a comment line at the top of your crontab reminding you of the order is a common, sensible habit.

crontab
# ┌ minute (0-59)
# │ ┌ hour (0-23)
# │ │ ┌ day of month (1-31)
# │ │ │ ┌ month (1-12)
# │ │ │ │ ┌ day of week (0-6, Sun=0)
# │ │ │ │ │
0 3 * * * /opt/app/backup.sh # 3:00 AM every day
*/15 * * * * /usr/local/bin/health-check.sh # every 15 minutes
0 0 1 * * /opt/app/monthly-report.sh # midnight on the 1st of each month

Editing your crontab

crontab -e opens your personal crontab in an editor; save and the schedule is live immediately. crontab -l lists your current jobs. Each user has their own crontab, and there are also system-wide schedules in /etc/crontab and /etc/cron.d/. For a beginner, crontab -e for your own jobs and crontab -l to check them is all you need to start automating tasks.

terminal
$ crontab -e # edit YOUR crontab (opens $EDITOR; save to apply)
$ crontab -l # list your current scheduled jobs
0 3 * * * /opt/app/backup.sh
*/15 * * * * /usr/local/bin/health-check.sh
$ crontab -r # remove all your cron jobs (careful — no undo)

The gotchas that bite everyone

Cron jobs fail in predictable ways, and knowing them up front saves hours. Cron runs with a minimal environment and a bare PATH, so a script that works in your shell may fail in cron because a command is not found or a variable is unset — always use full paths (/usr/bin/python3, not python3) and do not rely on your .bashrc. And cron output goes nowhere by default, so a failing job is silent; redirect output to a log file so you can see what happened.

crontab
# full paths + capture output, so failures are visible instead of silent
0 3 * * * /usr/bin/python3 /opt/app/backup.py >> /var/log/backup.log 2>&1
# └ full path └ full path └ append stdout+stderr to a log
A cron job that runs as root is a privilege boundary
From the security courses: a scheduled job that runs as root, where the script or a directory in its path is writable by a lower-privileged user, lets that user get their code executed as root — one of the most common real-world privilege-escalation paths. As a beginner, the habits that keep you safe are: run cron jobs as the least-privileged user that can do the work (not root unless necessary), keep the scripts they run owned by root and not world-writable, and use full paths so an attacker cannot shadow a command via PATH. Automation is powerful, and so is automation an attacker can hijack.