Cron is the default scheduler on almost every Linux distribution. It runs quietly in the background, executes commands at set intervals, and requires no extra daemons. Yet, many developers and sysadmins still struggle with the syntax or permissions when they first try to automate a backup or a log rotation. This guide walks you through the process from scratch. No fluff, just the commands and checks you need to get a job running and verified.
1. Understand the Cron Daemon
Before touching a config file, know what you are dealing with. The cron daemon runs as a system service. It wakes up every minute, checks for jobs that are due, and executes them. If the service is stopped, nothing runs. This is the first thing to check if a job suddenly stops working. On modern systems using systemd, you can check its status quickly.
systemctl status cron
If it is inactive, start it with systemctl start cron. On some RHEL-based systems, the service is named crond. Know which one yours uses. Most Debian and Ubuntu systems use cron. RHEL, CentOS, and Fedora typically use crond. The commands are identical, but the service name differs.
2. Access the Crontab Editor
There are two main ways to manage cron jobs. You can edit the system-wide file at /etc/crontab, or you can use user-specific crontabs. For most tasks, user-specific is safer and simpler. It avoids permission issues with root-owned files. Open the editor for the current user with this command:
crontab -e
If this is your first time, it may ask you to choose an editor. Pick nano if you are unfamiliar with vi. It is friendlier for quick edits. The editor will open with a header comment. Ignore it. You add your lines below.
3. Master the Time Syntax
This is where most people get stuck. A cron line has six fields. The first five define time. The last is the command. The fields are: minute, hour, day of month, month, day of week. Separated by spaces. Here is the format:
* * * * * command
Each asterisk represents "any value." You can replace them with specific numbers or ranges. For example, 0 2 * * * runs at 2:00 AM every day. */15 * * * * runs every 15 minutes. 0 0 1 * * runs at midnight on the first day of every month. The day of week field uses 0-6, where 0 is Sunday. Some systems accept 7 as Sunday too, but stick to 0 for clarity.
4. Write Your First Command
Let’s create a simple test job. We will log the current date to a file. This helps verify that cron is actually executing the command. Add this line to your crontab:
* * * * * date >> /tmp/cron_test.log 2>&1
This runs every minute. The >> appends to the log file. The 2>&1 redirects standard error to standard output, so you catch errors too. Save and exit the editor. Now, wait one minute. Check the log file. If you see a timestamp, cron is working. If the file is empty, something is wrong. Do not assume it is working just because the syntax looks right. Verify it.
5. Handle Environment Variables
Cron runs in a minimal environment. It does not inherit your shell’s PATH or HOME variables. This causes a common issue: commands like mysql or php fail because cron cannot find them. The fix is to use absolute paths. Instead of php script.php, use /usr/bin/php /path/to/script.php. You can also define variables at the top of your crontab. For example:
PATH=/usr/local/bin:/usr/bin:/bin
[email protected]
Set PATH to include directories where your binaries live. Set MAILTO if you want email notifications on failure. If you do not want email, set MAILTO="". This suppresses output emails. Many hosts disable cron email by default, so check your mail server settings if you expect notifications.
6. Use Absolute Paths for Everything
This cannot be stressed enough. Relative paths almost never work in cron. The working directory is usually the user’s home directory, but it can vary. Never rely on it. If your script is in /var/www/backup.sh, write /var/www/backup.sh in the cron line. If it needs to write to /var/log/backup.log, use the full path. If it calls a binary in /opt/custom/bin, add that to your PATH variable or use the full path in the command. Ambiguity is the enemy of automation.
7. Test with a Specific Time
Waiting for a scheduled time is tedious. For testing, use a job that runs every minute. Once it works, change the time to your desired schedule. Do not jump straight to a daily job. If it fails, you will not know if it is a cron issue or a script issue. Start small. Get the command running manually in a terminal. Then put it in cron. If it works in the terminal but not in cron, the issue is almost certainly environment-related. Check PATH, permissions, and working directory.
8. Check Permissions
The script must be executable. If it is not, cron will silently fail. Run this command to ensure it is executable:
chmod +x /var/www/backup.sh
Also, ensure the user running cron has read access to the script and write access to any output files. If the script writes to a directory owned by root, and you are running as a regular user, it will fail. Change ownership or create a writable directory. Use ls -l to check permissions. The execute bit (x) must be set. The owner must match the user running the cron job.
9. Monitor Logs for Failures
Cron does not send you an error message when a job fails. It just does not run. Or it runs and fails silently. To debug, check the system logs. On most systems, cron logs go to /var/log/syslog or /var/log/cron. Use grep to find entries related to your job. For example:
grep CRON /var/log/syslog | tail -20
Look for lines that show the command being executed. If you see the command but no output, the script ran. If you see nothing, cron did not trigger the job. Check the syntax again. A missing field or a typo in the time specification will prevent execution. Also, check if the cron service is running. A stopped daemon means no jobs run, period.
10. Secure Your Crontab
Crontabs are stored in /var/spool/cron/ or /var/spool/cron/crontabs/, depending on the distro. These files are sensitive. Anyone with root access can modify them. Ensure the permissions are correct. The user should own the file. Only root and the user should have read/write access. Do not make your crontab world-readable. You can check permissions with:
ls -l /var/spool/cron/crontabs/username
Also, avoid storing sensitive data like passwords in plaintext in your crontab. If your script needs credentials, use environment variables or a separate config file with restricted permissions. Do not hardcode secrets in the cron line. It is visible to anyone who can read the crontab file.
What if the job runs but does nothing?
This usually means the command executed but produced no output. Or the output went somewhere you are not looking. Check the redirection in your command. If you used > file.log, it overwrites the file each time. If you used >> file.log, it appends. If you did not redirect, output goes to stdout, which cron may discard or email. Add 2>&1 to capture errors. Then check the log file. If the file is empty, the script likely exited early. Add logging inside your script to trace execution.
How do I prevent overlapping runs?
Cron does not lock jobs by default. If a job takes longer than its interval, the next instance will start while the first is still running. This can cause data corruption or resource exhaustion. Use flock to lock the job. For example:
* * * * * /usr/bin/flock -n /tmp/backup.lock /var/www/backup.sh
The -n flag makes it non-blocking. If the lock is held, the new instance exits immediately. This ensures only one instance runs at a time. Adjust the lock file path to your preference.
What to check first
If your cron job is not working, start with these three checks. First, verify the syntax. Count the fields. Ensure the time specification matches your intent. Second, check the service status. Is cron or crond running? Third, look at the logs. Did cron attempt to run the command? If yes, check the script’s output and permissions. If no, the issue is in the schedule or the daemon. Do not guess. Use the logs. They tell you exactly what happened and when. Cron is reliable, but only if you configure it correctly. Take the time to test each step. It saves hours of frustration later.
