How to Monitor Cron Jobs in PHP with Cronaman
The silent failure problem in PHP
PHP cron jobs are everywhere — running on cPanel shared hosting, Plesk VPS instances, and inside Laravel and WordPress installations. They send invoices, sync inventory, generate reports, and purge expired sessions. And they are, quietly, some of the hardest jobs to monitor.
Unlike a web request that returns a 500 status code, a failing PHP cron job produces nothing. The script exits with a non-zero code that nobody reads. Output redirected to /dev/null. No alert. No ticket. You find out when a client asks why their invoice didn't arrive.
What is heartbeat monitoring?
Heartbeat monitoring turns the problem around. Instead of reacting to failures, you configure Cronaman with the expected interval — say, every hour — and your PHP script sends an HTTP ping at the end of every successful run. If the ping doesn't arrive, Cronaman sends you an email. Job crashed, server rebooted, cron entry deleted: all caught.
No extension to install. No library to require. One HTTP call — that's it.
Create a Cronaman monitor
Start by creating the monitor in Cronaman:
- Sign up at cronaman.dev — free plan, no credit card required
- Click New Monitor
- Name it (e.g., "Send invoices") and set the interval to match your cron schedule
- Copy your ping URL:
https://cronaman.dev/ping/php-invoice
Pinging with file_get_contents
file_get_contents is available on virtually every PHP installation — including shared hosting that blocks cURL. It's the simplest way to send a ping:
<?php
define('PING_URL', 'https://cronaman.dev/ping/php-invoice');
function sendInvoices(): void {
// Your job logic here
echo "Sending invoices...
";
// ... query DB, generate PDFs, call Resend/SendGrid ...
}
try {
sendInvoices();
@file_get_contents(PING_URL);
} catch (Exception $e) {
echo "Job failed: " . $e->getMessage() . "
";
exit(1);
}The @ suppresses PHP warnings if the ping fails (e.g., DNS hiccup) — you don't want a transient network error to mask a successful job run. Note that allow_url_fopen must be enabled in your PHP config, which is the default on most hosts.
Using cURL for more control
If you need a timeout or more control over the HTTP request, cURL gives you both:
<?php
define('PING_URL', 'https://cronaman.dev/ping/php-invoice');
function ping(string $url): void {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);
}
function sendInvoices(): void {
echo "Sending invoices...
";
// ... your job logic here ...
}
try {
sendInvoices();
ping(PING_URL);
} catch (Exception $e) {
echo "Job failed: " . $e->getMessage() . "
";
exit(1);
}Always set CURLOPT_TIMEOUT. A cron job that hangs on an HTTP call can block the next run if the scheduler doesn't enforce a time limit. 10 seconds is plenty for a simple ping.
Signaling failure explicitly
Append /fail to your ping URL to mark the run as failed immediately, without waiting for the grace period. This is the pattern to use when you handle exceptions explicitly:
<?php
define('PING_URL', 'https://cronaman.dev/ping/php-invoice');
define('FAIL_URL', 'https://cronaman.dev/ping/php-invoice/fail');
function ping(string $url): void {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}
try {
// Your job logic here
sendInvoices();
ping(PING_URL); // success
} catch (Exception $e) {
echo "Job failed: " . $e->getMessage() . "
";
ping(FAIL_URL); // signal failure immediately
exit(1);
}Scheduling on cPanel / shared hosting
In cPanel, go to Cron Jobs and add your script. A common pattern:
/usr/bin/php /home/user/public_html/cron/send_invoices.php >> /dev/null 2>&1Set the frequency in cPanel to match the interval you configured in Cronaman. Add a grace period of 5–10 minutes to handle load variation on shared servers — they're sometimes slow to spin up PHP.
Laravel scheduled commands
Laravel's scheduler is the cleanest way to manage PHP cron jobs. Add a Cronaman ping using the after callback, which fires after the command completes successfully:
<?php
use IlluminateSupportFacadesHttp;
use IlluminateSupportFacadesSchedule;
Schedule::command('invoices:send')
->hourly()
->after(function () {
Http::get('https://cronaman.dev/ping/php-invoice');
});Laravel's Http facade is always available — no extra imports needed. The after hook only runs when the command exits successfully, so the ping is a genuine success signal. For Laravel 11+, this goes in routes/console.php; for Laravel 10 and below, use the App\Console\Kernel class.
Verify your setup
Run the script once from the command line to confirm the ping fires:
php send_invoices.phpOpen the Cronaman dashboard. The monitor should show "healthy" within a few seconds. If it stays grey, verify that allow_url_fopen is enabled (for file_get_contents) or that the cURL extension is loaded (for curl). On cPanel, you can check both in the PHP configuration section.
Once confirmed, your PHP cron jobs are monitored. Missed run, exception, server issue — you'll get an email immediately, before anyone else notices.
More cron monitoring guides
Using a different language? The pattern is the same everywhere:
- How to Monitor Cron Jobs in Python — covers urllib, requests, and crontab
- How to Monitor Cron Jobs in Node.js — covers native fetch, Axios, and node-cron
More guides
What Is Cron Job Monitoring? (And Why You Need It)
5 min readProBeyond Timing: Catch Silent Successes with Semantic Ping Payloads
5 min readPythonHow to Monitor Cron Jobs in Python with Cronaman
6 min readNode.jsHow to Monitor Cron Jobs in Node.js with Cronaman
7 min readBashHow to Monitor Cron Jobs in Bash with Cronaman
5 min readRubyHow to Monitor Cron Jobs in Ruby with Cronaman
6 min readGoHow to Monitor Cron Jobs in Go with Cronaman
6 min readLaravelHow to Monitor Laravel Cron Jobs with Cronaman
6 min readStart monitoring your PHP cron jobs
Free forever for up to 3 monitors. No credit card required. Set up in under 2 minutes.
Start monitoring free