Fix: Cron Job Not Running on Linux
Quick Answer
How to fix cron jobs not running on Linux — caused by PATH issues, missing newlines, permission errors, environment variables not set, and cron daemon not running.
The Error
You set up a cron job but it never runs. There is no error message — cron silently does nothing. Or the cron job appears to run (you see it in logs) but the command has no effect.
Common symptoms:
- The cron job runs manually from the terminal but not automatically via cron.
- A script that works fine when run directly fails silently when cron runs it.
crontab -lshows the job, but nothing happens at the scheduled time.- The cron job ran once and stopped working.
- Cron runs at the wrong time despite the correct-looking schedule.
Why This Happens
Cron runs in a minimal, non-interactive environment that differs significantly from your normal shell session:
- Minimal PATH: cron’s default PATH is typically
/usr/bin:/bin— most user-installed commands are not available. - No environment variables:
HOME,USER,DISPLAY, variables from.bashrc/.bash_profileare not set. - No shell expansion quirks: cron uses
/bin/shby default, not bash. - Missing newline at end of crontab: cron requires a trailing newline — the last job is silently ignored without one.
- Permission issues: scripts must be executable, and the cron user must have access.
- Cron daemon not running:
crondorcronservice may be stopped. - Wrong syntax: the cron time expression has a typo.
Fix 1: Check and Start the Cron Daemon
If cron is not running, no jobs execute:
# Check cron status (systemd-based systems)
sudo systemctl status cron # Debian/Ubuntu
sudo systemctl status crond # RHEL/CentOS/Fedora
# Start and enable cron
sudo systemctl start cron
sudo systemctl enable cron # Auto-start on boot
# Verify it's running
ps aux | grep cronOn Docker containers, cron is often not installed or not running:
# Install and start cron in a Debian/Ubuntu container
apt-get install -y cron
cron & # Start in background (no systemd in containers)Or add cron to your Dockerfile and start it in your entrypoint:
RUN apt-get update && apt-get install -y cron
COPY my-crontab /etc/cron.d/my-crontab
RUN chmod 0644 /etc/cron.d/my-crontab && crontab /etc/cron.d/my-crontab
CMD ["cron", "-f"]Fix 2: Fix PATH — Use Full Paths for All Commands
Cron’s default PATH is /usr/bin:/bin. Commands like node, python3, pip, npm, custom scripts — anything outside /usr/bin and /bin — are not found.
Broken — command not found in cron:
*/5 * * * * node /app/sync.js
# cron cannot find 'node' — it's in /usr/local/bin, not /usr/binFix 1 — set PATH at the top of the crontab:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/nvm/versions/node/v20/bin
*/5 * * * * node /app/sync.jsFix 2 — use full paths for every command:
*/5 * * * * /usr/local/bin/node /app/sync.js
0 2 * * * /usr/bin/python3 /home/user/scripts/backup.pyFind the full path of a command:
which node # /usr/local/bin/node
which python3 # /usr/bin/python3
which npm # /usr/local/bin/npmPro Tip: Always use full paths for commands in cron jobs. It is the single most reliable fix and makes the crontab self-documenting. Relying on PATH in cron is fragile — the path changes depending on how cron was configured and which user runs it.
Fix 3: Verify Crontab Syntax
Cron syntax: minute hour day-of-month month day-of-week command
┌───────────── minute (0–59)
│ ┌───────────── hour (0–23)
│ │ ┌───────────── day of month (1–31)
│ │ │ ┌───────────── month (1–12)
│ │ │ │ ┌───────────── day of week (0–7, 0 and 7 = Sunday)
│ │ │ │ │
* * * * * commandCommon syntax mistakes:
# Wrong — runs at minute 30 of every hour, not "every 30 minutes"
30 * * * * /path/to/script.sh
# Correct — runs every 30 minutes
*/30 * * * * /path/to/script.sh
# Wrong — no such time (hour 25)
0 25 * * * /path/to/script.sh
# Correct — midnight
0 0 * * * /path/to/script.sh
# Run every weekday at 9 AM
0 9 * * 1-5 /path/to/script.sh
# Run on the 1st and 15th of each month
0 0 1,15 * * /path/to/script.shValidate your cron expression using an online tool like crontab.guru before adding it to your crontab.
Ensure there is a newline at the end of the crontab. The last line of a crontab must end with a newline (\n). If it does not, cron silently ignores the last job. When editing with crontab -e, press Enter at the end of the last line before saving.
Fix 4: Redirect Output to Debug
By default, cron sends output to the user’s local mail. On most servers, local mail is not configured, so output is silently discarded. Redirect stdout and stderr to a file:
*/5 * * * * /path/to/script.sh >> /var/log/myscript.log 2>&1>>appends stdout to the log file.2>&1redirects stderr to the same place as stdout.
Check cron’s own logs:
# Ubuntu/Debian
grep CRON /var/log/syslog
# RHEL/CentOS
grep CRON /var/log/cron
# journald (systemd)
journalctl -u cron --since "1 hour ago"
journalctl -u crond --since "1 hour ago"Cron logs show when jobs run and whether they started successfully — even if the job itself fails.
Fix 5: Fix Environment Variables
Scripts that depend on environment variables set in .bashrc, .bash_profile, or .env files will fail in cron — those files are not sourced:
Broken — relies on .bashrc variables:
#!/bin/bash
# script.sh — expects DATABASE_URL to be set
psql "$DATABASE_URL" -c "SELECT 1"Fix 1 — source the environment file in the script:
#!/bin/bash
source /home/user/.env # Load env vars from file
psql "$DATABASE_URL" -c "SELECT 1"Fix 2 — set variables directly in the crontab:
DATABASE_URL=postgres://user:pass@localhost/mydb
REDIS_URL=redis://localhost:6379
0 2 * * * /home/user/scripts/backup.shVariables set in the crontab apply to all jobs in that crontab.
Fix 3 — use env in the command:
0 2 * * * env DATABASE_URL=postgres://user:pass@localhost/mydb /home/user/scripts/backup.shFor .env file loading issues in application code, see Fix: .env variables not loading.
Fix 6: Fix Script Permissions and Shell Issues
Make the script executable:
chmod +x /home/user/scripts/backup.sh
# Verify permissions
ls -la /home/user/scripts/backup.sh
# -rwxr-xr-x — the x bits must be setAdd a shebang line to the script:
Without a shebang, the system may use /bin/sh instead of bash, and bash-specific syntax fails:
#!/bin/bash
# Rest of the script/bin/sh vs /bin/bash: cron uses /bin/sh by default. On some systems, /bin/sh is dash (not bash), which does not support [[, source, arrays, and other bash-specific features. Either:
- Change the script’s shebang to
#!/bin/bash - Or override cron’s shell at the top of the crontab:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
*/5 * * * * /home/user/scripts/myscript.shFix 7: Fix User and Permission Context
Cron jobs run as the user whose crontab they are in. If the script accesses files owned by root or requires elevated privileges:
Running as root (root’s crontab):
sudo crontab -e # Edit root's crontabOr use /etc/cron.d/ with a user column:
# /etc/cron.d/myapp — system crontab format includes the user field
*/5 * * * * www-data /var/www/app/scripts/cleanup.sh
0 2 * * * root /usr/local/bin/backup.shCheck which user’s crontab you are editing:
crontab -l # Your user's crontab
sudo crontab -l # Root's crontab
crontab -u www-data -l # Another user's crontabVerify the script runs as the expected user:
# Add this temporarily to verify the cron user
* * * * * whoami >> /tmp/cron-user.txt 2>&1Fix 8: Test the Exact Cron Environment
The most reliable way to debug cron jobs: simulate the exact cron environment and run your command manually:
# Run your script with the minimal cron environment
env -i HOME=/home/user \
PATH=/usr/bin:/bin \
SHELL=/bin/sh \
USER=yourusername \
/home/user/scripts/myscript.shIf the script fails here, it will also fail in cron. Fix the errors revealed in this simulated environment.
Or run a test job immediately:
# Add this — runs every minute for testing
* * * * * /full/path/to/command >> /tmp/cron-test.log 2>&1Watch /tmp/cron-test.log for output. Remove the test job after debugging.
Still Not Working?
Check for cron.deny or cron.allow files. If /etc/cron.allow exists, only users listed in it can use cron. If /etc/cron.deny exists, listed users cannot use cron:
cat /etc/cron.allow 2>/dev/null
cat /etc/cron.deny 2>/dev/nullCheck for SELinux or AppArmor restrictions. On systems with SELinux (RHEL/CentOS) or AppArmor (Ubuntu), security policies may block cron jobs from accessing files or running commands:
# Check SELinux denials
sudo ausearch -m avc -ts recent | grep cron
# Check AppArmor
sudo aa-status
sudo journalctl | grep apparmor | grep cronCheck disk space. If the filesystem is full, cron cannot write its log or create temp files, causing silent failures. See Fix: Docker no space left on device for disk space debugging techniques.
Check for timezone mismatches. Cron uses the system timezone by default. If your server is in UTC but you expect jobs to run in your local time, set TZ in the crontab:
TZ=America/New_York
0 9 * * * /path/to/morning-report.shFor systemd-based systems, systemd timers are often a more reliable alternative to cron — they support logging, dependencies, and better error handling. Consider migrating persistent cron jobs to systemd timers for production workloads.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: EMFILE Too Many Open Files / ulimit Error on Linux
How to fix EMFILE too many open files errors on Linux and Node.js — caused by low ulimit file descriptor limits, file handle leaks, and how to increase limits permanently.
Fix: Linux OOM Killer Killing Processes (Out of Memory)
How to fix Linux OOM killer terminating processes — reading oom_kill logs, adjusting oom_score_adj, adding swap, tuning vm.overcommit, and preventing memory leaks.
Fix: Certbot Certificate Renewal Failed (Let's Encrypt)
How to fix Certbot certificate renewal failures — domain validation errors, port 80 blocked, nginx config issues, permissions, and automating renewals with systemd or cron.
Fix: Docker Compose Environment Variables Not Loading from .env File
How to fix Docker Compose not loading environment variables from .env files — why variables are empty or undefined inside containers, the difference between env_file and variable substitution, and how to debug env var issues.