Every night at 1:30 a.m., while I'm asleep, my server dumps every database it runs, packs them up, and files them away. At 2:15 it snapshots its own configuration. Every fifteen minutes, all day, it checks its own pulse. I didn't write a fancy scheduler to do any of that. It's all cron: a tool older than most of the people reading this, and still the most useful thing on my server.
If you've just rented your first VPS, cron is the first "real admin" skill worth learning. It's small, it's everywhere, and once it clicks you'll start seeing chores everywhere that you can hand to it.
What cron actually is
Cron is an alarm clock for commands. You give it a time and a command; when the time comes, it runs the command. That's the whole idea. There's a small background program (the cron daemon) that wakes up once a minute, looks at its list of jobs, and runs anything that's due.
Each person (user account) on a server has their own list, called a crontab (short for "cron table"). You see yours with:
crontab -l
and edit it with:
crontab -e
That opens the list in a text editor. One line equals one job. Save, close, done. There's no restart and no "apply" button.
Reading the five stars
Every cron line starts with five fields that say when, followed by the command that says what. This is the part that looks like line noise until someone explains it once:
┌───────── minute (0–59)
│ ┌─────── hour (0–23)
│ │ ┌───── day of month (1–31)
│ │ │ ┌─── month (1–12)
│ │ │ │ ┌─ day of week (0–7, Sunday is 0 or 7)
│ │ │ │ │
30 1 * * * /root/maint/db-backup.sh
A * means "every". So that line reads: at minute 30 of hour 1, every day of every month, on any weekday, or in human words, 1:30 a.m. daily. Here are the patterns I actually use, which cover about 95% of what you'll ever need:
| Schedule | Means |
|---|---|
30 1 * * * | Every day at 1:30 a.m. |
0 4 * * 1 | Mondays at 4:00 a.m. |
*/10 * * * * | Every 10 minutes |
7,22,37,52 * * * * | Four times an hour, at those exact minutes |
0 1 * * 0 | Sundays at 1:00 a.m. |
20 4 1 * * | The 1st of every month at 4:20 a.m. |
When in doubt, paste your line into crontab.guru. It translates it into plain English, and I still use it after years of doing this.
My actual cron setup
Here's what runs on the one VPS that hosts this site and about eighteen others. I've grouped it by what the jobs are for, which is a better way to think about cron than by time of day.
Protecting data
# Dump every MySQL and PostgreSQL database, keep 14 days
30 1 * * * /root/maint/db-backup.sh
# Snapshot the server's config folder (/etc)
15 2 * * * tar -czf ~/backups/etc-$(date +\%Y\%m\%d).tar.gz -C / etc
# Copy all of it, encrypted, to Google Drive
30 4 * * * /root/maint/offsite-sync.sh
Watching the server
# Pulse check: disk, memory, services, containers, backups
7,22,37,52 * * * * /root/maint/health-check.sh
# Does every website load? Are SSL certificates about to expire?
0 8 * * * /root/maint/site-check.sh
# Monday morning "here's how your server is doing" email
30 7 * * 1 /root/maint/weekly-summary.sh
Cleaning up after itself
# Delete website logs older than a week
30 3 * * * find ~/domains/*/logs/ -name "*.log" -mtime +7 -delete
# Delete backups older than two weeks
35 3 * * * find ~/backups/ -name "*.tar.gz" -mtime +14 -delete
Doing actual work
# Weekly: refresh a resource directory one of my sites depends on
0 3 * * 1 /opt/scraper/run.sh
Notice the times are staggered: 1:30, 2:15, 3:30, 3:35, 4:30. That's on purpose. The backup needs to finish before the offsite copy starts, and I don't want three heavy jobs fighting over the disk at exactly midnight. Picking slightly odd minutes (7, 22, 37, 52) also keeps my jobs from lining up with everyone else's on-the-hour jobs, which matters more than you'd think on shared hardware.
The day I found three broken jobs
Here's the part nobody tells beginners: cron fails silently. It doesn't pop up an error. It doesn't turn anything red. If a job breaks, it just keeps breaking, every night, forever, until you go looking.
I recently sat down and audited my own crontab, line by line, asking "is this actually working?" Three out of six jobs on one account weren't:
- A backup job pointing at a script that didn't exist. The line looked completely legitimate. The file it called just… wasn't there. Every night it failed, and every night it sent a failure notice to an inbox I wasn't reading.
- A weekly malware scan that had never run once. It compared files against a timestamp file in
/tmp. That file had never been created, so the comparison errored out, so the step that would have created the file never ran. A perfect little loop of nothing. - A config backup that "worked" but threw errors nightly. It ran as a regular user who wasn't allowed to read a few protected files, so it skipped them and exited with an error code every single time.
None of these were dramatic. All of them would have mattered on the day I needed them. The lesson I took away is that a cron job you never hear from isn't necessarily a healthy one. It might be one nobody's listening to.
Rule I now follow: every important job either emails me when it fails, or is checked by something that does. And one job emails me every Monday no matter what, because if that email ever stops arriving, I know the alerting itself is broken.
Beginner gotchas (I've hit all of these)
- Cron has a tiny PATH. A command that works in your terminal can fail in cron with "command not found", because cron doesn't load your shell setup. Use full paths (
/usr/bin/python3, notpython3) or setPATH=at the top of the crontab. - The
%sign is special. In a crontab,%means "newline". That's why mydatecommand above is written+\%Y\%m\%dwith backslashes. Forget them and the job silently does something weird. - Which user runs it matters. A job in your personal crontab runs as you, with your permissions. My config backup "failed" because of exactly this.
- Server time isn't your time. Check with
date. Many servers run on UTC, so "2 a.m." might be the middle of your afternoon. - Output goes somewhere. Anything a job prints gets emailed to the address in
MAILTO=at the top of the crontab. Set it to an inbox you read. Or redirect output to a log file with>> /path/to/log 2>&1so you can check later. - Test the command by hand first. Run it exactly as written, as the same user, before scheduling it. Half of all cron problems show up right there.
Where else cron jobs hide
Your personal crontab isn't the only list. When you audit a server, check all of these:
crontab -lfor your user, andsudo crontab -lfor root/etc/cron.d/: one file per job set, often installed by software (my firewall and control panel both put jobs here)/etc/cron.daily,cron.weeklyand friends: scripts dropped in a folder, run on that schedulesystemctl list-timers: systemd timers, the modern alternative to cron. One of my apps uses these instead- Your control panel. DirectAdmin and cPanel both have a "Cron Jobs" page, which is just a friendly editor for the same crontab underneath
Start with these three
If you're setting up your first server and want a place to begin, these three jobs give you the most peace of mind for the least effort:
- A nightly backup of your databases and app data.
- A nightly cleanup that deletes old backups and logs, so the first job doesn't eventually fill your disk.
- A failure alert on the backup, so you find out the day it breaks, not the day you need it.
Cron won't make your server smarter. It'll make it consistent, doing the boring, important stuff every single night whether you remember or not. That's the whole trick to running a server without it running you.