Fixing ‘command not found’ in Linux Cron Jobs: PATH Variable Mismatches from macOS Development
Troubleshoot Linux cron jobs failing on macOS dev environments due to minimal PATH settings. Learn to define robust PATHs for consistent execution across environments.
Troubleshoot Linux cron jobs failing on macOS dev environments due to minimal PATH settings. Learn to define robust PATHs for consistent execution across environments.
When developing or testing shell scripts on your macOS local environment for deployment as cron jobs on a Linux server, it's common to encounter issues where scripts fail unexpectedly. A frequent culprit is the PATH environment variable, which operates very differently in an interactive shell versus the non-interactive, minimal environment provided by cron. This guide will help you diagnose and resolve "command not found" errors that often arise from PATH discrepancies, which can sometimes be misidentified as a "syntax error" related to the environment.
Symptom & Error Signature
Your cron job might appear to run, but either nothing happens, or you receive an email (if MAILTO is configured) with output indicating that commands within your script cannot be found. On macOS, if you're testing cron locally, the output might be less verbose without explicit redirection.
Typical error messages you might encounter in your cron logs or email output include:
Subject: Cron <user@hostname> /path/to/your/script.sh
Content-Type: text/plain; charset=UTF-8
Auto-Submitted: auto-generated
X-Cron-Env: <... various env vars ...>
X-Cron-Env: PATH=/usr/bin:/bin
/bin/sh: line 1: php: command not found
Or, if your script attempts to use a command like mysqldump or node without its full path:
Subject: Cron <user@hostname> /path/to/your/script.sh
Content-Type: text/plain; charset=UTF-8
Auto-Submitted: auto-generated
X-Cron-Env: PATH=/usr/bin:/bin
/path/to/your/script.sh: line 5: mysqldump: command not found
While the problem description mentions a "PATH variable syntax error," this is usually a symptom of a "command not found" issue. A true PATH syntax error would imply malformed string assignment, which is rarer than the more common issue of missing directories in the PATH leading to unfound executables.
Root Cause Analysis
The core of this problem lies in the fundamental differences between your interactive shell environment and the non-interactive shell environment that cron provides:
- Minimal
cronEnvironment: Whencronexecutes a job, it does so with a very basic, stripped-down set of environment variables. Crucially, thePATHvariable is often limited to/usr/bin:/binon Linux systems (and similar on macOS, e.g.,/usr/bin:/bin:/usr/sbin:/sbin). This is significantly different from the richPATHyou experience in your terminal session. - Shell Initialization Files Ignored: Your interactive shell (
bash,zsh, etc.) sources configuration files like~/.bashrc,~/.zshrc,~/.profile, or~/.bash_profile. These files often containexport PATHcommands that add directories (e.g.,/usr/local/bin,~/bin, Homebrew paths like/opt/homebrew/binon macOS) to yourPATH.crondoes not source these files. - macOS vs. Linux
PATHDiscrepancies: Tools installed on macOS, especially via Homebrew, reside in paths like/usr/local/binor/opt/homebrew/bin. These directories are typically not present incron's defaultPATHon either macOS or Linux. On a Linux server, package-managed tools are usually in/usr/bin,/usr/local/bin, orsbindirectories. A script that works flawlessly on your macOS development environment with its expandedPATHwill fail on a Linuxcronenvironment if it relies on commands whose executables are not incron's restrictedPATH. - Perceived "Syntax Error": If your script attempts to manipulate the
PATHvariable itself (e.g.,export PATH=$PATH:/custom/path) and the initial$PATHprovided bycronis unexpected, or if a critical command needed for subsequentPATHmodification isn't found, it can lead to shell errors that might be misinterpreted as a "syntax error" in thePATHassignment. However, it's almost always acommand not foundissue resulting from an inadequatePATH.
Step-by-Step Resolution
To ensure your cron jobs run reliably on your Linux production servers, you need to explicitly define the PATH and other necessary environment variables.
1. Verify cron's Default PATH Environment
First, understand what cron sees. Add a temporary cron job to dump the environment:
# On your Linux server (or macOS for local testing)
crontab -e
Add the following line, ensuring it runs once or twice, then remove it:
* * * * * env > ~/cron_env.txt 2>&1
Wait a minute or two, then check the contents of ~/cron_env.txt. You'll typically see a very minimal PATH:
# Excerpt from ~/cron_env.txt
SHELL=/bin/sh
USER=youruser
PATH=/usr/bin:/bin
PWD=/home/youruser
LANG=C
HOME=/home/youruser
LOGNAME=youruser
...
This confirms cron uses a highly restricted PATH.
2. Identify Missing Commands and Their Full Paths
For any command that fails with "command not found," determine its full path in your interactive shell (where it works).
which php
# Output might be: /usr/bin/php (on Linux) or /usr/local/bin/php (on macOS with Homebrew)
which mysqldump
# Output might be: /usr/bin/mysqldump (on Linux) or /usr/local/bin/mysqldump (on macOS with Homebrew)
If a command is found in a Homebrew path on macOS (e.g.,
/opt/homebrew/bin/php), be aware that this path and executable may not exist on your Linux server. You'll need to use the Linux equivalent path (e.g.,/usr/bin/php).
3. Explicitly Define PATH in the Cron Job or Script
This is the most robust solution. You have two primary options:
Option A: Define PATH at the top of your crontab
You can set environment variables directly within your crontab file. This PATH will apply to all subsequent cron jobs in that crontab.
crontab -e
Add your desired PATH at the top of the file, before any job definitions. Be comprehensive, including common system paths and any custom binary paths your script relies on.
# Example crontab entry
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin # Add any other paths your script needs
# Mailable output for errors
MAILTO="[email protected]"
# My cron jobs
* * * * * /path/to/your/script.sh
0 0 * * * /usr/bin/php /var/www/html/mysite/artisan schedule:run >> /var/log/laravel-cron.log 2>&1
While defining
PATHincrontabis effective, make sure it is exhaustive enough for all jobs listed. If a specific job needs an even more specializedPATH, it's better to define it within the script itself (Option B).
Option B: Define PATH within the Script (Highly Recommended)
For maximum portability and robustness, explicitly define PATH (and any other necessary environment variables) at the beginning of your shell script. This ensures the script always runs with the expected environment, regardless of the crontab's PATH.
#!/bin/bash
# Script: /path/to/your/script.sh
# --- Start Environment Setup ---
# Define a robust PATH for the script's execution environment.
# Include all directories where your script's commands might reside.
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:$PATH"
# If your script needs specific variables (e.g., for PHP frameworks or Docker)
# export APP_ENV=production
# export DOCKER_HOST=unix:///var/run/docker.sock
# --- End Environment Setup ---
# Example: Running a PHP Artisan command (common in web hosting environments)
# Note: Use full paths for critical commands even with PATH defined for ultimate reliability.
/usr/bin/php /var/www/html/mysite/artisan schedule:run
# Example: Running a custom Python script
/usr/bin/python3 /opt/my_app/scripts/cleanup.py
# Example: Using mysqldump
/usr/bin/mysqldump -uuser -ppassword database > /var/backups/database.sql
Always use the full, absolute path to executables within your script (e.g.,
/usr/bin/phpinstead ofphp) for critical commands, even if you definePATH. This completely eliminatesPATHresolution issues for those specific commands and improves script reliability.
4. Ensure Correct Shell Invocation & Shebang
cron often defaults to /bin/sh (which might be dash on Debian/Ubuntu, a minimal POSIX-compliant shell). If your script uses bash-specific syntax, it needs to be explicitly invoked with bash.
- Shebang: Ensure your script starts with a correct shebang line.
#!/bin/bash # Or for more portability if bash is not always in /bin: #!/usr/bin/env bash - Crontab
SHELLvariable: You can also setSHELL=/bin/bashat the top of yourcrontabfile to makebashthe default for all jobs.
5. Redirect Output for Debugging
Cron jobs run silently by default, making debugging difficult. Always redirect stdout and stderr to a log file or to email.
- Log to file:
This appends both standard output and standard error to* * * * * /path/to/your/script.sh >> /var/log/cron_script.log 2>&1/var/log/cron_script.log. - Mail output:
Set
MAILTOat the top of yourcrontabto receive any output (including errors) via email.MAILTO="[email protected]" * * * * * /path/to/your/script.sh
For
MAILTOto work, your Linux server needs a properly configured Mail Transfer Agent (MTA) like Postfix, Sendmail, or a lightweight alternative likemsmtpto send outbound mail.
6. Test on the Target Linux Environment
It's crucial to test your cron job on the actual target Linux server environment where it will run.
- Simulate cron: Manually run your script using a simulated
cronenvironment from the command line:
AdjustSHELL=/bin/bash HOME=/home/youruser USER=youruser LOGNAME=youruser PATH="/usr/bin:/bin" /path/to/your/script.shPATHto match whatcronprovides (or what you intend to provide via your crontab/script). This is an excellent way to debugPATHand environment issues before putting the job intocrontab. - Use
atcommand: For a one-time test that closely mimicscron's environment (but uses the current user's environment slightly more), theatcommand can be useful:
Check theecho "/path/to/your/script.sh" | at now + 1 minuteatjob's output in your mail spool (/var/mail/youruseror similar).
By diligently following these steps, you can eliminate PATH related "command not found" errors and ensure your Linux cron jobs, developed on macOS, execute reliably in their production environments.
Our Production Verification Guarantee
Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.