Linux Cron Job Not Running: `env PATH` Variable Syntax Error on Ubuntu 20.04 LTS Troubleshooting Guide

Diagnose and resolve 'command not found' errors in Linux cron jobs on Ubuntu 20.04 LTS due to incorrect PATH environment variable configuration.


Diagnose and resolve 'command not found' errors in Linux cron jobs on Ubuntu 20.04 LTS due to incorrect PATH environment variable configuration.

Cron jobs are the backbone of automated task execution on Linux systems, powering everything from database backups to log rotation and application maintenance scripts. However, a common pitfall encountered by both new and experienced administrators is a cron job failing to run, often silently, with symptoms pointing to commands not being found, even though they execute perfectly from an interactive shell. This guide specifically addresses issues on Ubuntu 20.04 LTS related to the PATH environment variable, including potential syntax errors when attempting to configure it within cron, leading to the dreaded "command not found" error.

Symptom & Error Signature

The primary symptom is that your scheduled task simply does not execute or fails with an error indicating that a command within your script cannot be found. You won't typically see a direct "PATH variable syntax error" message unless you've explicitly introduced one while trying to fix the PATH. Instead, the underlying issue (an insufficient PATH) manifests as:

  • Email Notifications (if MAILTO is configured):

    Subject: Cron <user@hostname> /path/to/your/script.sh
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    X-Cron-Env: <SHELL=/bin/sh>
    X-Cron-Env: <HOME=/home/user>
    X-Cron-Env: <PATH=/usr/bin:/bin>
    X-Cron-Env: <LOGNAME=user>
    X-Cron-Env: <USER=user>
    Message-Id: <...>
    Date: Mon, 9 Aug 2026 00:00:00 +0000 (UTC)
    
    /path/to/your/script.sh: line 5: some_command: command not found
    

    Note the X-Cron-Env: <PATH=/usr/bin:/bin> line, indicating a very minimal PATH.

  • System Logs (/var/log/syslog): You might see entries indicating the cron job started, but no successful completion or specific error messages within syslog itself, as the output is usually redirected or mailed.

    Aug  9 00:00:00 hostname CRON[12345]: (user) CMD (/path/to/your/script.sh)
    Aug  9 00:00:00 hostname CRON[12344]: (user) END_CMD (/path/to/your/script.sh)
    

    The actual command not found error would be in the email or a file if output was redirected.

  • No visible output or job execution: The most insidious symptom is simply the absence of the expected outcome (e.g., website data not updated, backups not created) without any obvious error messages.

Root Cause Analysis

The core of this problem lies in the fundamentally different environment in which cron jobs execute compared to your interactive shell.

  1. Minimal PATH Environment: When you log into an interactive shell (e.g., via SSH), your PATH environment variable is typically populated by configuration files like /etc/profile, ~/.bashrc, ~/.profile, etc. This PATH includes directories where various commands (like npm, php, python3, composer, wp, docker, custom binaries) are located. Cron, however, runs jobs in a very minimal, non-interactive shell environment (/bin/sh or /bin/bash without loading user-specific startup files). Its default PATH is extremely limited, often just /usr/bin:/bin or /usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin. If your script tries to call a command not in these default directories (e.g., php from /usr/local/php8.2/bin, node from ~/.nvm/versions/node/vX.Y.Z/bin, or docker which might be in /usr/bin but relies on other environment variables), cron will fail to find it.

  2. Lack of Environment Inheritance: Cron jobs do not inherit environment variables from the user's interactive session. This means any PATH modifications, custom variables, or aliases defined in your ~/.bashrc or ~/.profile are completely ignored by cron.

  3. PATH Syntax Errors in Crontab: While not a direct error code, users attempting to fix the PATH issue often introduce syntax errors when trying to set the PATH variable directly within the crontab entry itself. Common mistakes include:

    • Using export PATH="..." directly on the command line of a cron job entry (the export keyword is generally unnecessary and sometimes problematic in a single-line crontab command).
    • Incorrectly expanding existing PATH variables within a single line (e.g., PATH=$PATH:/new/path; command). While PATH=/new/path:$PATH can work when defined at the top of the crontab, doing it inline can be tricky.
    • Using unquoted paths with spaces (though less common in PATH itself).
    • Typographical errors or incorrect colon separators.

The solution involves explicitly providing cron with the necessary PATH information, either directly in the crontab or via a wrapper script.

Step-by-Step Resolution

Here's how to systematically troubleshoot and resolve PATH issues for cron jobs on Ubuntu 20.04 LTS.

1. Verify Cron Execution & Capture Output

First, ensure cron is running your job at all, and direct its output for inspection.

  1. Check System Logs:

    grep CRON /var/log/syslog | tail -n 50
    

    Look for entries related to your user and command. This confirms if cron tried to run your job.

  2. Redirect Cron Job Output: Modify your crontab entry to explicitly redirect all output (stdout and stderr) to a log file. This is crucial for debugging.

    # Original (failing) entry example:
    # * * * * * /path/to/your/script.sh
    
    # Modified entry to capture output:
    * * * * * /path/to/your/script.sh >> /var/log/my_cron_job.log 2>&1
    

    After the next scheduled run, check /var/log/my_cron_job.log for specific errors like command not found.

  3. Configure MAILTO: If you haven't already, add a MAILTO variable at the top of your user's crontab to receive error messages directly via email (assuming your system's mail daemon is configured).

    MAILTO="[email protected]"
    # ... rest of your crontab entries ...
    

    On Ubuntu, you might need to install a mail client like mailutils (sudo apt install mailutils) and configure a local MTA (e.g., Postfix) to send external emails. For simple local debugging, MAILTO="youruser" will send mail to your local Linux user.

2. Understand Cron's Default Environment

Let's see exactly what PATH cron is using.

  1. Create a Temporary Cron Job: Add a temporary entry to your crontab (using crontab -e) to output cron's environment variables to a file:
    * * * * * env > /tmp/cron_env.log 2>&1
    
  2. Inspect the Output: Wait a minute, then check the contents of /tmp/cron_env.log:
    cat /tmp/cron_env.log
    
    You'll likely see a very minimal PATH like:
    SHELL=/bin/sh
    PWD=/home/youruser
    LOGNAME=youruser
    HOME=/home/youruser
    LANG=en_US.UTF-8
    PATH=/usr/bin:/bin
    _=/usr/bin/env
    
    Compare this PATH with your interactive shell's PATH:
    echo $PATH
    
    You'll notice the interactive shell's PATH is much richer.

3. Best Practice: Use Absolute Paths

The simplest and most robust fix for command not found is to always use the absolute path to executables within your cron job or script.

  1. Find the Absolute Path: In your interactive shell, use the which command to find the absolute path of the failing command:
    which php
    # Expected output: /usr/bin/php (or /usr/local/bin/php, /opt/php/bin/php, etc.)
    
    which node
    # Expected output: /usr/bin/node (or ~/.nvm/versions/node/vX.Y.Z/bin/node, etc.)
    
    which composer
    # Expected output: /usr/local/bin/composer
    
  2. Update Your Crontab Entry: Replace the command name with its absolute path:
    # Original (failing):
    # * * * * * php /var/www/html/script.php
    
    # Corrected with absolute path:
    * * * * * /usr/bin/php /var/www/html/script.php >> /var/log/my_cron_job.log 2>&1
    

    If your command is within a user's home directory (e.g., .nvm for Node.js), the absolute path will be /home/youruser/.nvm/versions/node/vX.Y.Z/bin/node.

4. Define PATH within the Crontab File

If using absolute paths for every command is cumbersome, you can define a custom PATH variable at the top of your crontab file. This PATH will apply to all subsequent cron entries in that crontab.

  1. Edit Crontab: Open your crontab for editing:
    crontab -e
    
  2. Add PATH Definition: At the very top of the file (before any cron job entries), add a PATH line. You should include cron's default paths and any additional directories required.
    MAILTO="[email protected]"
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/home/youruser/.nvm/versions/node/v16.14.0/bin:/usr/local/php8.2/bin
    
    # Your cron job entries below:
    * * * * * php /var/www/html/script.php >> /var/log/my_cron_job.log 2>&1
    0 1 * * * node /home/youruser/app/worker.js >> /var/log/my_node_worker.log 2>&1
    

    • The PATH line must start with PATH= and contain colon-separated directory paths.
    • Do not use export on this line within crontab. The PATH variable is implicitly exported to all subsequent cron jobs.
    • Ensure you include the standard system paths (/usr/bin:/bin, etc.) in addition to your custom paths, or you might break other commands. You can get a good base by running echo $PATH in your interactive shell and adding necessary custom paths.

5. Use a Wrapper Script for Complex Jobs

For jobs involving multiple commands, specific environment variables, or complex logic, a wrapper shell script is the most robust and maintainable solution.

  1. Create the Wrapper Script: Create a new shell script (e.g., run_my_app.sh) in a suitable location (e.g., ~/bin or /opt/scripts).

    #!/bin/bash
    
    # --- Set Custom Environment Variables (including PATH) ---
    export PATH="/home/youruser/.nvm/versions/node/v16.14.0/bin:/usr/local/php8.2/bin:$PATH"
    export NODE_ENV="production"
    # Add any other required environment variables
    
    # --- Navigate to the Application Directory (if needed) ---
    cd /var/www/html/my_project || { echo "Failed to change directory" >&2; exit 1; }
    
    # --- Execute Your Commands ---
    # Example 1: Laravel scheduler
    /usr/bin/php artisan schedule:run >> /var/log/laravel_cron.log 2>&1
    
    # Example 2: Node.js application
    node index.js >> /var/log/node_app_cron.log 2>&1
    
    # Example 3: WordPress WP-CLI
    wp cron event run --due-now >> /var/log/wp_cron.log 2>&1
    

    • The #!/bin/bash shebang ensures the script runs with Bash, allowing more advanced shell features.
    • Use export for environment variables within the script, as you would in a regular shell script.
    • You can prepend your custom paths to the existing PATH using export PATH="/your/custom/path:$PATH".
    • Always redirect output to a log file within the script for easier debugging.
  2. Make the Script Executable:

    chmod +x /path/to/your/run_my_app.sh
    
  3. Update Crontab: Your crontab entry now simply calls the wrapper script:

    # Crontab entry
    * * * * * /path/to/your/run_my_app.sh
    

    This method encapsulates all environment setup and command execution logic, making your crontab cleaner and the job more robust.

6. Debugging PATH Syntax Errors (If You Tried to Set It Incorrectly)

If you've attempted to set PATH directly in a cron command and observed new errors, review the syntax.

  • Incorrect (Often Leads to Errors):

    # This might work for simple commands, but is prone to errors, especially with expansions
    * * * * * export PATH=/my/custom/path:$PATH; /usr/bin/mycommand
    

    The export keyword is generally redundant and can sometimes confuse sh when used in this inline context. Also, $PATH expansion within a single command line entry can be unreliable or require careful escaping depending on the shell cron uses.

  • Better (Inline, but still less ideal than a wrapper or global PATH):

    # Prepend PATH for a single command, without 'export'
    * * * * * PATH=/my/custom/path:/usr/bin:/bin /usr/bin/mycommand
    

    This sets PATH for the duration of that single command.

    For consistency and maintainability, prefer defining PATH at the top of your crontab (Step 4) or using a wrapper script (Step 5) over complex inline PATH modifications in individual cron job lines.

7. Test and Validate

After making changes:

  1. Wait for the next scheduled run or, for quick testing of a specific command, adjust the cron schedule to run in the next minute.
  2. Check your log files (/var/log/syslog, /var/log/my_cron_job.log, etc.) or your email (MAILTO) for success messages or any new errors.
  3. Verify the desired outcome of the cron job (e.g., check database updates, file changes, service restarts).

By following these steps, you should be able to precisely diagnose and resolve PATH related issues preventing your cron jobs from running correctly on Ubuntu 20.04 LTS.