Proper Syntax for Running a Cron Job Every 4 Hours (Not Every Minute) – Troubleshooting Guide


3 views

Many developers accidentally use this problematic syntax when attempting to run a job every 4 hours:

* */4 * * * /cmd.sh

This actually makes the job run every minute of every 4th hour (hours divisible by 4), rather than running once every 4 hours as intended.

Here are the proper ways to schedule a cron job every 4 hours:

0 */4 * * * /path/to/command

or more explicitly:

0 0,4,8,12,16,20 * * * /path/to/command

The asterisk (*) in the minute field means "every minute" while */4 in the hour field means "every 4 hours". The combination executes the job every minute during the specified hours.

Always check these aspects when troubleshooting:

  • Cron service running status: sudo service cron status
  • User permissions for the cron job
  • File permissions for the script being executed
  • Cron logs: grep CRON /var/log/syslog

Here's a full example with error handling:

# Run backup script every 4 hours
0 */4 * * * /usr/bin/flock -n /tmp/backup.lock /bin/bash /scripts/backup.sh >> /var/log/backup.log 2>&1

The flock prevents overlapping executions if a previous run hasn't finished.

When troubleshooting, consider these environmental factors:

# Specify full paths and environment
0 */4 * * * . /home/user/.profile; /usr/bin/python3 /scripts/process_data.py

Many Linux administrators encounter this exact issue when trying to schedule jobs at 4-hour intervals. The incorrect syntax:

* */4 * * * /cmd.sh

This actually runs your command every minute during active hours, not every 4 hours as intended. The asterisk in the minutes field is the culprit.

The correct format should specify both minute and hour fields precisely:

0 */4 * * * /path/to/your/command

This will execute at minute 0 of every 4th hour (e.g., 00:00, 04:00, 08:00, etc.).

For more complex scheduling needs, consider these variations:

# Every 4 hours offset by 30 minutes
30 */4 * * * /path/to/command

# Specific hours listing (more readable)
0 0,4,8,12,16,20 * * * /path/to/command

To test your crontab without waiting:

# Check cron logs (location varies by distro)
grep CRON /var/log/syslog
# or
journalctl -u cron.service

# Verify environment variables are set
* * * * * env > /tmp/cronenv
  • Always use absolute paths in scripts
  • Redirect output to log files for debugging
  • Set proper permissions on scripts (755 recommended)
  • Consider using systemd timers for complex schedules

Here's a complete working example for database backups:

0 */4 * * * /usr/bin/mysqldump -u root -pPASSWORD dbname > /backups/db-$(date +\%Y\%m\%d-\%H\%M).sql 2>&1

Note the escaped percent signs and output redirection.