Hi there! I'm Shrijith Venkatrama, founder of Hexmos. Right now, I’m building LiveAPI, a first of its kind tool for helping you automatically index API endpoints across all your repositories. LiveAPI helps you discover, understand and use APIs in large tech infrastructures with ease.
Cron is one of those tools that’s been around forever, quietly powering scheduled tasks on servers everywhere. It’s simple, reliable, and deceptively powerful. But most developers only scratch the surface, setting up basic schedules and calling it a day. There’s so much more you can do with cron to make your life easier, from debugging to chaining complex jobs. This post dives into practical cron tricks with detailed examples to level up your automation game.
Understanding Cron Syntax Inside Out
Cron’s syntax can feel like a puzzle at first, but once you get it, it’s straightforward. A cron expression has six fields (seconds are optional in some implementations):
* * * * * * <command>
| | | | | |
| | | | | +---- Day of week (0-7, Sunday is 0 or 7)
| | | | +------ Month (1-12)
| | | +-------- Day of month (1-31)
| | +---------- Hour (0-23)
| +------------ Minute (0-59)
+-------------- Second (0-59, optional)
Each field accepts numbers, ranges, steps, or lists. For example, 0 15 * * 1-5
runs a job at 3:00 PM Monday through Friday. The key is precision: misplace a star or number, and your job might run at the wrong time—or not at all.
Here’s a quick example to schedule a Python script every weekday at 8 AM:
0 8 * * 1-5 /usr/bin/python3 /home/user/scripts/backup.py
# Runs backup.py at 8:00 AM, Monday through Friday
If you’re new to cron, test your expressions with tools like Crontab.guru. It’s a lifesaver for verifying schedules.
Debugging Cron Jobs Like a Pro
Cron jobs can fail silently, which is frustrating. Logs are your best friend. By default, cron sends output (stdout and stderr) to the user’s email, but you can redirect it to a file for easier debugging. Add >>
to your cron command to log output:
0 0 * * * /usr/bin/python3 /home/user/scripts/report.py >> /home/user/logs/report.log 2>&1
# Runs report.py at midnight, logs output and errors to report.log
Check the system logs too. On most Linux systems, cron logs to /var/log/syslog
or /var/log/cron
. Use grep CRON /var/log/syslog
to see what’s happening.
Another trick is to test your script manually before scheduling it. Run it in the same environment cron uses (same user, same shell). For example:
sudo -u cronuser /bin/bash -c "/usr/bin/python3 /home/user/scripts/report.py"
# Simulates cron’s environment for cronuser
This ensures your script handles paths and permissions correctly.
Chaining Multiple Commands in One Job
Sometimes you need a cron job to run multiple commands in sequence. You can chain them with &&
or ;
in a single line. Use &&
for commands that depend on the previous one succeeding, and ;
for independent commands.
Here’s an example that backs up a database and then syncs it to a remote server:
0 2 * * * /usr/bin/pg_dump mydb > /backups/db_backup.sql && /usr/bin/rsync -az /backups/db_backup.sql user@remote:/backups/
# Dumps PostgreSQL database at 2 AM and syncs to remote server if dump succeeds
Operator | Use Case | Example |
---|---|---|
&& |
Run next command only if previous succeeds | cmd1 && cmd2 |
; |
Run commands sequentially, regardless of success | cmd1 ; cmd2 |
Pro tip: If one command fails with &&
, the rest won’t run, so use it when order and success matter.
Using Environment Variables for Flexibility
Cron runs in a minimal environment, so your scripts might miss variables like PATH
or PYTHONPATH
. You can set these directly in the crontab file before your command. This makes scripts portable and easier to maintain.
Example with a Python script needing a custom PATH
:
0 5 * * * PATH=/usr/local/bin:/usr/bin:/bin; PYTHONPATH=/home/user/libs; /usr/bin/python3 /home/user/scripts/process_data.py
# Runs process_data.py at 5 AM with custom PATH and PYTHONPATH
You can also source a shell script to load variables:
0 6 * * * . /home/user/.env.sh; /usr/bin/python3 /home/user/scripts/run_job.py
# Sources .env.sh before running run_job.py at 6 AM
Check out the cron manual for more on environment settings.
Running Jobs at System Startup
Sometimes you need a task to run when the system boots, like starting a monitoring script. Use the @reboot
directive instead of a time-based schedule.
Example to start a Node.js server on reboot:
@reboot /usr/bin/node /home/user/app/server.js >> /home/user/logs/server.log 2>&1
# Starts server.js on system boot, logs output to server.log
Important: Test your @reboot
scripts carefully, as they run in a bare environment. Ensure absolute paths and proper permissions.
Avoiding Overlapping Jobs with Lock Files
Long-running jobs can overlap if a new instance starts before the previous one finishes. This can cause issues like corrupted data. A simple fix is to use a lock file to ensure only one instance runs at a time.
Here’s a Bash script that uses a lock file:
#!/bin/bash
LOCKFILE=/tmp/myjob.lock
if [ -e "$LOCKFILE" ]; then
echo "Job already running"
exit 1
fi
touch "$LOCKFILE"
# Do work here
sleep 10
echo "Job completed" >> /tmp/myjob.log
rm "$LOCKFILE"
# Script sleeps for 10 seconds, logs completion to myjob.log
Schedule it in cron:
* * * * * /bin/bash /home/user/scripts/no_overlap.sh
# Runs no_overlap.sh every minute, but skips if lock file exists
This ensures the script doesn’t run again until the lock file is removed.
Scheduling Complex Intervals with Steps and Lists
Cron’s step (/
) and list (,
) operators let you create complex schedules. Steps run jobs at intervals within a range, like every 10 minutes. Lists let you specify specific values, like running at 9 AM, 12 PM, and 3 PM.
Example for running a cleanup script every 15 minutes:
*/15 * * * * /bin/bash /home/user/scripts/cleanup.sh
# Runs cleanup.sh at 0, 15, 30, and 45 minutes past every hour
Example for running a report at specific hours:
0 9,12,15 * * * /usr/bin/python3 /home/user/scripts/report.py
# Runs report.py at 9 AM, 12 PM, and 3 PM daily
Operator | Description | Example |
---|---|---|
/ |
Step values (every N units) |
*/10 (every 10 minutes) |
, |
List specific values |
9,12,15 (at 9, 12, and 15) |
Monitoring Cron Jobs with Health Checks
You want to know when a cron job fails or stops running. A simple way is to have your script send a health check to a monitoring service or log a status. For example, use curl
to ping a health endpoint after a job runs.
Example with a health check:
0 3 * * * /usr/bin/python3 /home/user/scripts/process_data.py && curl -fsS --retry 3 https://hc-ping.com/12345
# Runs process_data.py at 3 AM and pings health check if successful
You can use services like Healthchecks.io to track job status. If the ping doesn’t arrive, you get an alert.
Making Cron Work for You
Cron is a workhorse, but these tricks make it a powerhouse. From debugging with logs to preventing overlaps with lock files, you can tailor cron to fit complex workflows. Experiment with these techniques, test thoroughly, and always check your logs. Got a favorite cron hack? Drop it in the comments on Dev.to—I’d love to hear how you’re using it.
Top comments (0)