It usually starts with a small, forgettable task. You write a script that backs up a folder, or clears out old log files, or emails yourself a summary. It works beautifully — the first time, when you run it by hand. Then a week later you realize you forgot to run it for three days straight, the logs have ballooned, and the "system" you built is really just you remembering to type a command.
This is the exact problem cron was invented to solve. Cron is the scheduling service that has quietly run the world's Linux servers for decades. It does one thing: run a command at a time you specify, over and over, without you being there. Once you understand it, a whole category of "I keep forgetting to do X" chores simply disappears.
Cron doesn't make your scripts smarter. It makes them reliable — and reliability is most of what separates a hobby script from something you can depend on.
What cron actually is
On almost every Linux and macOS machine, a background process called cron wakes up every minute and checks a set of tables called crontabs. Each line in a crontab is a scheduled job: a time specification plus a command to run. If the current minute matches a line's schedule, cron runs that command. That's the entire model.
Each user on the system has their own crontab, and there's a system-wide one too. You almost never edit the file directly. Instead you use the crontab command, which opens your personal schedule in an editor and validates it when you save:
# Open your crontab for editing
crontab -e
# List what you currently have scheduled
crontab -l
# Remove your crontab entirely (be careful)
crontab -rThe first time you run crontab -e, it may ask which editor to use. Pick nano if you're unsure — it's the friendliest. Everything you add here runs as you, with your permissions, which is usually what you want for personal automation.
Reading the five-star schedule
The part that intimidates people is the timing syntax, but it's just five fields separated by spaces, always in the same order. Here's the map:
* * * * * command to run
│ │ │ │ │
│ │ │ │ └─ day of week (0-7, where 0 and 7 both mean Sunday)
│ │ │ └─── month (1-12)
│ │ └───── day of month (1-31)
│ └─────── hour (0-23)
└───────── minute (0-59)An asterisk means "every value." So * * * * * means every minute of every hour of every day — the busiest possible schedule. To pin a field, you replace the star with a number. A job that runs at 6:30 every morning looks like this:
30 6 * * * /home/me/scripts/backup.shRead it right to left in plain English: every day, every month, every day-of-week, at hour 6, minute 30. The two most common beginner mistakes are swapping the minute and hour fields, and forgetting that the fields are minute-first, not hour-first like a clock. When in doubt, say the line out loud in that order.
The shortcuts that cover most real needs
You rarely need surgical precision. Cron supports three operators that handle the vast majority of schedules. Lists use commas, ranges use a hyphen, and steps use a slash. A few examples make the pattern obvious:
# Every 15 minutes, all day
*/15 * * * * /home/me/scripts/poll.sh
# At the top of every hour, only 9am-5pm, Monday to Friday
0 9-17 * * 1-5 /home/me/scripts/business-hours.sh
# Twice a day: midnight and noon
0 0,12 * * * /home/me/scripts/sync.sh
# The 1st of every month at 3am
0 3 1 * * /home/me/scripts/monthly-report.shMany systems also accept human-readable nicknames that replace all five fields. @daily (midnight), @hourly, @weekly, @monthly, and @reboot (once, when the machine starts up) are the handy ones. If a plain @daily at midnight is good enough, use it — there's no prize for a more complicated line.
The silent-failure trap, and how to avoid it
Here is the single most important thing to know about cron, and the reason so many first jobs "don't work": cron runs your command in a stripped-down environment, not your normal shell. Your .bashrc isn't loaded. Your PATH is minimal. The working directory is your home folder, not wherever your script lives. A command that runs perfectly in your terminal can silently do nothing under cron because it can't find python3, or it looked for a file using a relative path that no longer points anywhere.
The fixes are simple once you know to apply them. Always use absolute paths — for the command, for the script, and for any files the script touches. And always capture the output, because otherwise cron mails errors to a local mailbox you'll never check. Redirecting both normal output and errors to a log file turns an invisible failure into a line you can actually read:
# Absolute paths everywhere, and log both stdout and stderr
30 6 * * * /usr/bin/python3 /home/me/scripts/report.py >> /home/me/logs/report.log 2>&1That 2>&1 at the end means "send error messages to the same place as normal output." The >> appends rather than overwriting, so you keep a running history. The first time a cron job misbehaves, the log almost always tells you exactly why — usually a missing path or a permission you forgot.
A worked example you can actually use
Let's build something end to end: a job that backs up a project folder every night and keeps the last seven days. First the script, saved with the executable bit set (chmod +x backup.sh):
#!/usr/bin/env bash
set -euo pipefail
SRC="/home/me/project"
DEST="/home/me/backups"
STAMP=$(date +%Y-%m-%d)
# Create today's compressed archive
tar -czf "$DEST/project-$STAMP.tar.gz" "$SRC"
# Delete archives older than 7 days
find "$DEST" -name 'project-*.tar.gz' -mtime +7 -deleteThen the crontab line that runs it at 2:15am every night and logs the result:
15 2 * * * /home/me/scripts/backup.sh >> /home/me/logs/backup.log 2>&1Notice the set -euo pipefail at the top of the script — it tells bash to stop immediately if any command fails, so a broken backup fails loudly in your log instead of half-finishing in silence. This pairing of a careful script plus a logged cron line is the pattern you'll reuse for almost everything.
When cron isn't the right tool
Cron is superb for time-based, recurring, independent tasks. It's a poor fit for a few situations, and knowing them saves frustration. If a job needs to run only after another job finishes, cron's fixed times make that fragile — reach for a real workflow tool or have one script call the next. If your machine is often asleep or powered off at the scheduled time (a laptop, say), the job simply won't fire; on Linux, systemd timers with the Persistent=true option can catch up on missed runs, which plain cron can't. And for sub-minute scheduling, cron's one-minute resolution is a hard floor.
For the enormous middle ground — nightly backups, hourly syncs, weekly cleanups, monthly reports — cron remains the simplest tool that does the job, and it's already installed on the machine in front of you.
The takeaway
Cron rewards a little upfront care and then asks almost nothing of you forever. Learn to read the five fields, lean on the comma-range-slash operators and the @daily-style nicknames, use absolute paths, and always log the output. Do that, and the class of problems that begins with "I keep forgetting to…" quietly turns into jobs that just run, night after night, whether you remember them or not.
Start with one small chore you're tired of doing by hand. Schedule it, check the log tomorrow, and enjoy the mild satisfaction of a computer doing your busywork exactly on time.
Comments 0