PHP Crontab Setup Guide: Fix Silent Failures & Path Errors
August 10, 2026


Why PHP Scripts Need Special Handling in Crontab
Crontab was built to run shell commands, not PHP applications, and that distinction causes most of the pain developers hit when running php script cron job entries for the first time. PHP needs an interpreter, and cron won't guess where it lives — you must hand it the exact path to the binary. PHP also behaves differently depending on how it's invoked: a script that runs cleanly in a browser can fail, hang, or silently do nothing when triggered by cron, because it's now running under PHP CLI instead of the web SAPI.
That mismatch — between how you tested the script and how cron actually executes it — is the root cause behind most "why doesn't my php crontab job run" questions. This guide walks through the setup correctly, then covers what happens after the job runs, which most tutorials skip.
Step 1: Find Your PHP Binary Path
Never assume /usr/bin/php is correct. On shared hosting with cPanel, multi-version servers, or any machine with PHP installed via a version manager, that path is frequently wrong. Run one of these:
which php
whereis php
php -v
which php shows the binary your shell currently resolves to. whereis php lists all installed PHP binaries, which matters if you have PHP 8.1 and 8.3 side by side and need a specific one. php -v confirms the version tied to whichever path you're about to use. On shared hosting, check your cPanel cron interface too — many hosts expose a PHP version selector or a documented path like /usr/local/bin/php. Skipping this step is the single most common reason a crontab php path ends up pointing at nothing.
Step 2: Write the Crontab Entry
Open your crontab with crontab -e and follow this pattern: schedule, absolute PHP path, absolute script path.
*/5 * * * * /usr/bin/php /var/www/myapp/scripts/sync.php
0 2 * * * /usr/local/bin/php8.2 /home/user/cron/cleanup.php
0 */6 * * * /usr/bin/php /var/www/myapp/artisan schedule:run
The first is a php crontab example every 5 minutes, useful for polling jobs. The second pins a specific PHP version explicitly, which matters when multiple versions coexist. The third runs Laravel's scheduler entry point, letting the framework handle its own internal schedule rather than cramming every task into raw crontab lines. If you're unclear on the five time fields, a standard cron syntax cheat sheet covers that ground better than re-explaining it here — the syntax itself isn't what trips people up. It's almost always the path, or what happens once PHP starts executing.
Both absolute paths matter. Relative paths depend on a working directory that cron doesn't set the way your shell does, so a script that runs fine manually can fail under cron. This absolute-path requirement is confirmed across PHP cron job setup guides for exactly this reason.
PHP CLI vs. Web SAPI: What Changes Under Cron
Generic cron tutorials fall short here, because they're written for shell commands without a separate runtime configuration. PHP has one. In a browser, PHP runs under a web SAPI (often mod_php, PHP-FPM) with one php.ini. Under cron, it runs under php cli cron with a different php.ini — often at a different path, with different extensions enabled, different memory_limit, and different max_execution_time.
A script that connects to a database fine on the web can fail via cron if the CLI build doesn't load the same database extension. Run php -m to list loaded modules for the CLI binary and compare it against your web SAPI's — a standard troubleshooting step covered in Cronjobpro's PHP cron jobs guide. Also remember: there's no $_SERVER['HTTP_HOST'], no session, no request context under CLI. If your script implicitly depends on web-only globals, it needs conditional logic to run standalone. Some teams sidestep this by triggering scripts over HTTP with curl or wget instead of invoking PHP directly — see this breakdown of HTTP-triggered scheduling options.
Logging Output So You Can Actually Debug Failures
By default, cron emails any output straight to the account owner, which gets ignored fast. Redirect stdout and stderr explicitly:
*/5 * * * * /usr/bin/php /var/www/scripts/sync.php >> /var/log/sync.log 2>&1
That appends both standard output and errors to one log file. For no output at all, redirect to /dev/null instead. To stop crontab email behavior more broadly, set MAILTO="" at the top of the crontab file, or keep the redirection above.
Logging fixes visibility into what happened during execution — it doesn't fix who's actually reading the log. A log file that fills up quietly on a server nobody checks is functionally the same as no log at all.
Avoiding Overlapping Runs
If a php crontab entry fires every five minutes but the script occasionally takes seven, you'll end up with two instances writing to the same resources simultaneously — a common source of race conditions, duplicate records, or corrupted state. The standard fix is a lock file using flock:
*/5 * * * * flock -n /tmp/sync.lock /usr/bin/php /var/www/scripts/sync.php
flock -n grabs an exclusive lock and exits immediately if one's already held, so the second invocation skips instead of running concurrently. This is a simple, dependency-free way to prevent overlapping cron jobs without rewriting the script, and it's specifically called out as a PHP CLI gotcha in cron setup references because PHP scripts — often doing I/O, external requests, or queue processing — are especially prone to variable runtimes.
The Blind Spot: Crontab Doesn't Tell You If the Job Worked
Everything above gets your script running reliably. None of it tells you whether the job did its job. A PHP script can exit with status 0 — success, as far as cron is concerned — while the API call inside it failed, the database write silently rolled back, or the file it was supposed to generate never got written. Cron only reacts to nonzero exit codes by emailing output, and only if you haven't suppressed it, and only if someone's reading that inbox.
This is cron job silent failure in practice: the schedule fires, the process runs, and nothing flags that the outcome was wrong. Log files help only as far as someone actively checks them, and most teams don't — until a missed run causes a real problem downstream.
This is exactly the gap Cronevra closes. Instead of building custom logging, alerting, and uptime checks around every scheduled script, Cronevra gives you cron job monitoring with execution history, failure alerts, and missed-run detection out of the box — so a script that quietly stops working gets flagged instead of forgotten.
Try Cronevra for Your PHP Cron Jobs
A correctly written crontab entry, with the right PHP path and solid logging, tells you a job ran. It doesn't tell you it worked. If you'd rather get notified the moment a PHP cron job fails or stops running — without maintaining your own alerting code — check out Cronevra or see pricing to get started.
Frequently Asked Questions
Why isn't my PHP cron job running even though the crontab entry looks correct?
The most common cause is an incorrect or missing absolute path to the PHP binary — cron doesn't inherit your shell's PATH, so php alone often resolves to nothing. Other frequent culprits include a missing absolute path to the script itself, incorrect file permissions, or a PHP CLI environment missing an extension the script needs. Check cron's own logs (often /var/log/syslog or /var/log/cron) to see if the job fired at all.
Do I need the full path to php in my crontab, or is 'php' enough?
You need the full absolute path, such as /usr/bin/php or whatever which php returns. Cron runs with a minimal environment that usually doesn't include the same PATH as your interactive shell, so a bare php command frequently fails silently or errors with "command not found."
How do I stop cron from emailing me every time a PHP script runs?
Redirect stdout and stderr in the crontab line itself, for example >> /var/log/job.log 2>&1, or set MAILTO="" at the top of the crontab file. Either approach stops cron's default behavior of emailing captured output to the crontab owner.
What's the difference between running a PHP script via cron and via a URL with curl or wget?
Running via cron invokes PHP CLI directly, using the CLI php.ini, CLI-enabled extensions, and no web request context like $_SERVER or sessions. Running via curl or wget hits your web server, so the script executes under the web SAPI with web-specific configuration — closer to how it behaves when a real user visits it.
How can I tell if a PHP cron job actually succeeded, not just that it ran?
A nonzero exit code is the most basic signal, but a script can exit 0 while still doing the wrong thing internally, like failing an API call it doesn't properly check. Reliable logging of key steps helps, but the more robust approach is external monitoring that tracks execution history and flags missed or failed runs — which is exactly what a tool like Cronevra provides.
Can two instances of the same PHP cron job run at the same time?
Yes, if the script's runtime occasionally exceeds its scheduled interval, cron will happily start a second instance while the first is still running. Wrapping the command with flock -n /tmp/job.lock prevents this by skipping the new invocation whenever a lock is already held.