A Linux cron job is a scheduled task that runs a command or script in the background according to a timetable you define. When you want repetitive work — backups, log cleanup, polling an API, or triggering Laravel's scheduler — to be fired by the system instead of by hand, cron is exactly the tool for it. In this guide I'll walk through crontab syntax, real-world examples, logging, and the pitfalls people trip over most.
cron, crontab, and the cron daemon
Keeping three terms apart makes everything clearer:
- cron: the always-running background service that watches the clock and fires jobs (the daemon, called
cronorcrondon most distros). - crontab: the "cron table", the config file where jobs are listed. Each user has their own crontab.
- cron job: a single line in that table — a schedule plus a command to run.
To edit your own crontab:
crontab -e # edit
crontab -l # list current jobs
crontab -r # remove all jobs (careful!)
Crontab syntax: the five fields
Each line is five time fields followed by the command:
# ┌───────── minute (0-59)
# │ ┌─────── hour (0-23)
# │ │ ┌───── day of month (1-31)
# │ │ │ ┌─── month (1-12)
# │ │ │ │ ┌─ day of week (0-7, 0 and 7 = Sunday)
# │ │ │ │ │
# * * * * * command-to-run
Special characters work in every field:
*— every value ("every minute", "every hour").,— a list:0,15,30,45.-— a range:1-5(Monday–Friday)./— a step:*/10(every 10 units).
Useful cron examples
The syntax can look abstract; concrete examples clear it up:
# Backup script every day at 02:30
30 2 * * * /home/aslain/scripts/backup.sh
# Health check every 5 minutes
*/5 * * * * /usr/bin/curl -fsS https://aslain.dev/health
# Report at 09:00 on weekdays
0 9 * * 1-5 /home/aslain/scripts/report.sh
# Log rotation at midnight on the 1st of each month
0 0 1 * * /home/aslain/scripts/rotate-logs.sh
# Laravel scheduler every 15 minutes
*/15 * * * * cd /var/www/site && php artisan schedule:run
You don't have to memorise the syntax: tools like crontab.guru translate an expression into plain English. Still, understanding the logic saves time when you're debugging.
Environment variables and absolute paths
The sneakiest thing about cron is that jobs run in a very bare environment. The PATH, aliases, and profile settings from your terminal don't exist in cron. That's why "it worked in my shell but not in cron" is such a common complaint.
- Write commands and files with absolute paths:
/usr/bin/phpinstead ofphp, full paths instead of./script.sh. - If your script needs it,
cdinto the right directory or hard-code the paths. - Declare the variables you need at the top of the crontab:
PATH=/usr/local/bin:/usr/bin:/bin
SHELL=/bin/bash
MAILTO=hello@aslain.dev
30 2 * * * /home/aslain/scripts/backup.sh
To find the right path, run which php or command -v node in your terminal and use the full path it prints.
Logging output and catching errors
By default cron tries to email a job's output to the user. If mail isn't configured, the output vanishes and you can't see what went wrong. The fix: redirect output to a file.
# write stdout and stderr to a log file
30 2 * * * /home/aslain/scripts/backup.sh >> /var/log/backup.log 2>&1
Here 2>&1 sends the error stream (stderr) to the same file as standard output (stdout), and >> appends instead of overwriting. For a silent job whose output you don't care about, use > /dev/null 2>&1 — but only after you're done debugging.
You can also confirm whether jobs actually fired from the system logs:
grep CRON /var/log/syslog # Debian/Ubuntu
journalctl -u cron --since today # systemd-based systems
Common mistakes
- The percent sign. In crontab,
%has special meaning (newline) and splits the command. Escape any%in date formats as\%. - Overlapping runs. A job that takes longer than its interval can overlap with the next. Lock it with
flock:flock -n /tmp/job.lock /home/aslain/scripts/job.sh. - Execute permission. Make sure the script is executable:
chmod +x script.sh, and give it a shebang (#!/bin/bash) on the first line. - Wrong user. Put the job in the crontab of whichever user has the right permissions; for
rootjobs usesudo crontab -e.
Frequently Asked Questions
When should I use a systemd timer instead of cron?
On modern systemd distros, timers are stronger for dependencies, delayed start, catching up missed runs (Persistent=true), and detailed logging. For simple periodic tasks cron is more than enough and available everywhere; for complex service-like schedules consider a systemd timer.
Can I run something every second?
No — cron's smallest unit is one minute. To run more often, use a loop with sleep inside a script, or switch to a systemd timer with OnUnitActiveSec.
What happens to jobs missed while the server is off?
Classic cron does not catch up missed runs; the slot is skipped while the machine is down. To run delayed jobs at boot, use anacron or the Persistent option of a systemd timer.
Are your scheduled tasks a mess? If you want the backups, deployments, and maintenance scripts on your server tied into reliable cron jobs, get in touch — let's build a tidy, logged setup together.