Guide PHP & Development

How to run a PHP script on a schedule

Cron runs a PHP script on a schedule with no browser involved — here is the five-field syntax and how to check a run actually worked.

Updated 7 min read Intermediate

Cron is the standard scheduler on Linux servers, and it is how you run a PHP script automatically — nightly cleanup, a report generated every morning, syncing data on a fixed interval — without anyone visiting a page to trigger it. On shared hosting you do not manage the underlying cron daemon yourself, but you can add and edit your own scheduled jobs through the cron job area of your control panel, and that requires no root access at all.

The five fields

Every cron schedule is five fields, each one a unit of time, followed by the command to run:

*    *    *    *    *   command-to-run
-    -    -    -    -
|    |    |    |    |
|    |    |    |    +----- day of week (0-6, Sunday=0)
|    |    |    +---------- month (1-12)
|    |    +--------------- day of month (1-31)
|    +-------------------- hour (0-23)
+------------------------- minute (0-59)

An asterisk means "every value of this field". A few concrete examples make the pattern clearer than the diagram alone:

ScheduleMeaning
* * * * *Every minute
*/15 * * * *Every 15 minutes
0 * * * *Once an hour, on the hour
0 3 * * *Once a day at 3am
0 3 * * 0Once a week, Sunday at 3am
0 0 1 * *Once a month, on the 1st at midnight

Running a PHP script from cron

The command itself calls the PHP binary directly against your script's file path, rather than requesting a URL:

0 3 * * * php /home/youraccount/scripts/nightly-cleanup.php
Use the full path to both PHP and the script

Cron does not run with the same environment as your interactive shell, so relying on a bare php without a full path can fail if more than one PHP version is installed and cron does not pick the one you expect. Where your account offers a specific versioned binary, use it directly — for example /usr/bin/php8.2 — to avoid ambiguity, and always use the full path to the script file itself rather than a relative one, since cron has no concept of "your current directory".

Logging what each run does

A cron job that runs silently gives you no way to tell a working run from one that failed halfway through. Redirect its output to a log file:

0 3 * * * php /home/youraccount/scripts/nightly-cleanup.php >> /home/youraccount/logs/cleanup.log 2>&1

>> appends output to the log rather than overwriting it on every run, and 2>&1 sends error output to the same file as ordinary output, so both appear together in the order they happened. Have the script itself write a clear line at the start and end of its own run — a timestamp and a simple "completed successfully" or an error detail — so a glance at the log tells you what happened without having to reconstruct it from raw output.

Timeouts inside a cron script

PHP run from the command line, including through cron, generally defaults to no execution time limit at all, unlike a script served through a browser — see increasing PHP max execution time, which covers the web-facing version of this same setting. That makes cron the right home for a job that genuinely takes several minutes, rather than trying to stretch a browser request's timeout to cover it.

Avoiding overlapping runs

A script scheduled every few minutes that occasionally takes longer than the interval between runs can end up with two copies running at once, which is rarely what you want — particularly for anything touching a database. A simple lock file guards against this:

<?php
$lock = '/home/youraccount/tmp/job.lock';
if (file_exists($lock)) {
    exit("Previous run still in progress.\n");
}
file_put_contents($lock, getmypid());
// ... do the work ...
unlink($lock);

This is a lightweight approach rather than a bulletproof one — a script that crashes without reaching the final unlink() leaves the lock file behind — but it is enough to prevent the common case of ordinary overlap on a job that occasionally runs a little long.

Calling a URL instead of a file path

Some setups schedule a script by having cron fetch a URL rather than calling the PHP binary against a file directly:

0 3 * * * curl -s https://yourdomain.com/cron/nightly-cleanup.php > /dev/null

This works, but it means the request travels over the public web the same as any visitor's would, so the endpoint has to defend itself — checking for a secret token in the request, or restricting the page to be callable only from your own server's IP address — rather than trusting that only cron will ever call it. Calling the script directly by file path, as in the examples above, avoids that exposure entirely, and is the better default unless your specific setup requires the URL-based approach.

When a scheduled run fails silently

If the log shows nothing at all, confirm the cron job is actually saved and enabled in your control panel, and check the path to both the PHP binary and the script are correct — a typo in either produces no output rather than a helpful error, because the shell fails before your script ever runs. If the log shows a PHP error, reading a PHP error log covers interpreting what it tells you.

Related reading