All about crons
Want to schedule a recurring command, such as a WordPress or Laravel scheduler?
This article explains how to create and configure crons in Core, and the behaviour worth knowing once they're running.
Creating a cron
Open a UNIX user, find the 'Crons' section, and click '+'.
Pick the matching 'Type' first — it fills in the schedule and the locking for you:
- 'WP-Cron (WordPress)' — pre-fills
wp --path=… cron event run --due-now, every minute, locking on. - 'Schedule (Laravel)' — pre-fills
php …/artisan schedule:run, every minute, locking off (Laravel handles its own withwithoutOverlapping). - 'Custom command' — write your own.
Which failure email did you get?
Also worth knowing before you start: don't append > /dev/null to the command.
Fields
Standard
- 'Name' — lowercase, digits, dash, underscore.
- 'Command' — what to run. The command may not call
exit. Let your script's normal exit code propagate. - 'Email Address' — where failure notifications go. Leave empty to send to Cyberfusion.
- 'Schedule' — a preset (every 5 minutes / minute / hour / 4 hours / day) or 'Custom' for a free-form cron expression.
- 'Locking Enabled' — prevents two instances of the same cron running at once. On by default. Turn it off only when the command handles its own locking.
'Active' toggles whether the schedule fires. 'Node' picks which node runs it.
Advanced options
- 'Error Count' — send an email after this many consecutive failures. The counter resets after any successful run. Default '1', so you get an email after every failure.
- 'Random Delay (s)' — randomise each run's start by up to this many seconds, so crons sharing a schedule don't all hit the node at the same instant. Default '10'.
- 'Timeout (s)' — kill the cron after this many seconds. A failsafe for commands that may hang, for example on a blocked external API call. Without a timeout, a hung run can sit forever — and with locking on, it blocks every subsequent run.
- 'Memory Limit (MiB)' — kill the cron if it exceeds this. Minimum 256.
- 'CPU Limit (cores)' — throttle the cron to at most this many CPU cores.
When you get emailed
A run has failed when the command exits with a return code other than 0. Failure emails contain that return code along with the command's output.
'exited with return code' emails
The most common one. The command exited non-zero and did not hit a timeout:
===== FATAL =====
Cron script command exited with return code <N>.
===== FATAL =====
Where the actual error is: the lines above that block. Everything between Running cron command and ===== FATAL ===== is what the command wrote to stdout or stderr during the failed run — a PHP stack trace, a wp-cli error, a command not found.
Those lines empty? The command produced no output before exiting. Add logging to the script, or run the command manually from a shell session to reproduce it.
Locking and 'still active' emails
===== FATAL =====
<PID> is still active. Stopping execution.
===== FATAL =====
The cause is always the same: the cron takes longer to complete than its schedule allows. Scheduled every 5 minutes but a run takes 7 — the run due at the 5-minute mark refuses to start, because the previous one is still going.
Three ways to fix it:
- Schedule the cron less often, or make the command finish faster.
- Set a 'Timeout (s)'. Without one, a stuck run holds the lock forever and every subsequent run sends this email until the process is killed.
- Turn 'Locking Enabled' off when the command handles concurrency itself. Laravel's scheduler is the common case:
withoutOverlappingalready prevents overlap, so Cyberfusion's lock is redundant and only produces these emails. The 'Schedule (Laravel)' type turns locking off for this reason.
A run already stuck and you cannot wait it out? Contact Cyberfusion support — the stuck process can be killed for you, so the cron starts running on schedule again.
Timeouts and 'timed out' emails
timeout: sending signal TERM to command '<path>'
===== FATAL =====
Cron script timed out
===== FATAL =====
The run took longer than 'Timeout (s)' allows, so it was killed.
Seeing this regularly? Either raise 'Timeout (s)' to match how long the command actually needs, or fix what is making it slow — typically a slow external API, a database lock, or unexpected load on the node.
Suppressing one-off failures
Set 'Error Count' higher than 1 and Cyberfusion only emails after that many consecutive failures. Any successful run resets the counter.
With 'Error Count' set to '3', a cron must fail three runs in a row before you get an email. One failure followed by a success sends nothing.
Use it when the cron is allowed to fail occasionally:
- Legacy projects that fail now and then but recover on the next run.
- Crons depending on flaky external APIs, where one failed call should not email you but ten in a row should.
Stop-spamming guard
After more than 10 consecutive failures, Cyberfusion stops sending failure emails until the cron succeeds again.
This cap applies on top of 'Error Count', whatever you set that to.
Where to find logs of previous runs
Every run is logged under cronscripts/logs/ in the home directory. Open it over SSH or SFTP.
Each cron keeps a rotating set of files:
<name>.log (most recent finished run)
<name>.log.0 (the one before)
<name>.log.1
<name>.log.2
…
A file named .log.TMP-<pid>.<suffix> is being written to right now, which means that cron is running.
Use these to answer "did this cron actually run last night?" or "what did the run an hour ago print?" without waiting for a failure email.
Don't redirect to /dev/null
Notifications fire only on a non-zero exit, not on output, so there is nothing to suppress. The 'Create Cron' modal warns you if it spots the redirection.
Redirecting does real harm: it throws away the debug output you will want when something does fail.
How this differs from system cron
Traditional system cron emails you whenever the command produces any output, on stdout or stderr, whether or not it succeeded. The exit code is ignored. The usual workaround is > /dev/null 2>&1, which also throws away output you would want for debugging.
Cyberfusion's crons work the other way around:
- Output is always written to a log file on the server.
- Emails fire only on a non-zero return code, never just because the command printed something.
So log freely from your script with echo, print or a framework logger, and read it all back from the log afterwards.
PHP and Node.js versions
The cron runs under the site's own account, so it uses that account's default PHP and Node.js versions. A bare php or node always resolves to the version set there, the same as in an interactive shell.
What crons are typically used for
- Client sites — the WP-Cron and Laravel Scheduler types cover almost every one. Both presets set the schedule and locking correctly without tweaking.
- Webshops — daily product feed exports, stock sync to marketplaces, abandoned-cart reminders. Set a sensible 'Timeout (s)' so a third-party API outage cannot leave the cron stuck for hours and block every subsequent run.
- Laravel and Symfony schedulers — dispatching queued jobs and periodic reconciliation: failed-payment retries, subscription renewals, daily digests. Pair with a daemon running the queue worker.
- Heavy nightly jobs — full-text index rebuilds, exports. Pin them to a specific 'Node' so they do not compete with HTTP workers on the main nodes.