Cron Setup Deep-Dive

Scheduled Tasks explains the engine. This page is the hands-on half: the command to enter on each hosting panel, the traps each panel sets, and how to prove the thing is running rather than assuming it.

The Server tab of settings, showing the runtime numbers
This screen tells you what the runtime is doing; the schedule itself lives in your host's cron.

Read this before you install anything

Scheduled publishing already works with no cron job at all. jekcms keeps a "next job due" timestamp in its cache directory and runs the engine right after a page response has been handed to a visitor. A post set for 14:37 goes live on the first visit at or after 14:37, and that visitor waits for nothing. On a normal request the whole mechanism costs one or two small file reads and no database query.

So the question is not "does it work without cron" but "what precision do I need". Real cron buys you three things: to-the-minute timing on a site with thin traffic, background work that happens even while nobody is browsing (newsletter batches, the weekly Search Console fetch, scheduled backups), and a clean answer when a client asks whether publishing depends on visitors.

The one case where you have no choice is a site with literally zero traffic, including your own admin sessions. Nothing can fire if nothing calls the site.

Install a real cron and jekcms notices: the CLI run leaves a freshness flag and the visitor-triggered scheduler switches itself off entirely for the next twenty minutes, refreshed on every tick. You can also force it off by defining JEK_DISABLE_PSEUDO_CRON as true in your config.

What to schedule

One command, once a minute, pointed at cron.php in your install root:

* * * * * /usr/bin/php /home/YOURUSER/public_html/cron.php >/dev/null 2>&1

Every five minutes is acceptable on a low-traffic site, at the cost of visible drift on scheduled publishes - a post set for 10:00 may appear at 10:04. Longer than that and the AI bulk-analysis worker, which deliberately processes one item per tick to stay inside provider rate limits, becomes uselessly slow.

The path to PHP varies. Try which php if you have shell access; otherwise /usr/local/bin/php and /opt/cpanel/ea-php82/root/usr/bin/php cover most shared hosts. Using the wrong binary is the single most common reason a cron job "runs" and does nothing.

cPanel

Advanced → Cron Jobs → Add New Cron Job. Pick Once per minute from the common settings dropdown and paste the command above with your own paths.

The >/dev/null 2>&1 matters here: without it cPanel emails you the output every single minute.

Hostinger hPanel

Advanced → Cron Jobs. Same command, but with a caveat measured on Hostinger in August 2026: hPanel does not reliably run the command through a shell, so >/dev/null 2>&1 is not always interpreted as a redirection. When that happens the tokens are handed to the program as ordinary arguments instead. PHP ignores them and cron.php still runs correctly, but the output is not silenced and the notification address fills up.

If you see a minute-by-minute mail flood after setting this up, drop the redirection from the command and set the cron job's notification address to empty in the panel instead. The same quirk is why jekcms's own deploy scripts log to a file themselves rather than trusting the panel's redirection.

Plesk

Tools & Settings → Scheduled Tasks → Add Task, task type Run a command:

php /var/www/vhosts/yourdomain.com/httpdocs/cron.php

Set Run to every minute and Send notifications to "Only on errors", otherwise Plesk mails you constantly. Click Run Now once after saving - it confirms the command works before you wait a minute to find out.

DirectAdmin

Advanced Features → Cron Jobs, same crontab syntax as cPanel:

* * * * * /usr/bin/php /home/USER/domains/yourdomain.com/public_html/cron.php >/dev/null 2>&1

Linux server (crontab)

On a VPS, edit the crontab of the user that owns the files - not root, or the cache and log files it writes will be unreadable by the web server afterwards:

sudo -u www-data crontab -e

Add a line that changes directory first and keeps a log you can read:

* * * * * cd /var/www/jekcms && /usr/bin/php cron.php >> logs/cron.log 2>&1

The cd matters because some includes resolve relative to the working directory, and the log redirect is what turns "cron seems broken" into a five-second diagnosis.

Linux + systemd timer

If you would rather have journal logs and reboot-safe state than a per-user crontab, create /etc/systemd/system/jekcms-cron.service:

[Unit]
Description=jekcms cron tick

[Service]
Type=oneshot
User=www-data
WorkingDirectory=/var/www/jekcms
ExecStart=/usr/bin/php cron.php

and /etc/systemd/system/jekcms-cron.timer:

[Unit]
Description=Run jekcms cron every minute

[Timer]
OnCalendar=*:0/1
Persistent=true

[Install]
WantedBy=timers.target

Then enable it and confirm the schedule landed:

sudo systemctl daemon-reload
sudo systemctl enable --now jekcms-cron.timer
systemctl list-timers | grep jekcms
journalctl -u jekcms-cron -n 20

Hosts with no shell and no cron panel

cron.php also accepts an HTTP trigger, but the secret travels in a header rather than the query string - query strings end up in access logs, browser history and Referer headers, which is not where a scheduler credential belongs.

Define CRON_TOKEN in your config, then point an external cron service at your site with a custom header:

curl -s -H "X-Cron-Secret: YOUR_CRON_TOKEN" https://yoursite.com/cron.php

Any request without a matching token gets 403 forbidden. Most external cron services (cron-job.org, EasyCron and similar) support custom headers on the free tier.

Windows Task Scheduler

For XAMPP, WAMP or IIS installs, use Create Task rather than Create Basic Task - only the full dialog exposes a minute-level repeat.

Under General, name it and tick Run whether user is logged on or not. Under Triggers, add a daily trigger starting today that repeats every 1 minute for a duration of 1 day. Under Actions, set the program to C:\xampp\php\php.exe, the argument to C:\xampp\htdocs\jekcms\cron.php, and Start in to C:\xampp\htdocs\jekcms - that last field is the one people forget, and without it relative includes fail. Under Conditions, untick Start only if the computer is on AC power or the task stops on a laptop. Under Settings, tick Run task as soon as possible after a scheduled start is missed.

Save, then right-click the task and choose Run to confirm it fires before trusting the schedule.

Proving it actually runs

Run the command by hand first. A CLI tick prints a timestamp, one line per step that did something, and done:

php /path/to/site/cron.php

If that works but the scheduled job does not, the problem is the panel entry, not jekcms. The freshness flag settles it - the file is touched on every real run:

ls -l cache/.cron-real.flag

A modification time within the last minute or two means cron is calling cron.php. An hours-old timestamp, or a missing file, means it is not, whatever the panel claims.

If you redirected output to a log, tail it:

tail -f logs/cron.log

And if you want to see what is actually waiting to be processed:

SELECT type, status, scheduled_at, attempts, error_message
FROM scheduled_tasks
WHERE status IN ('pending', 'processing')
ORDER BY scheduled_at;

Rows stuck in processing for more than half an hour are picked up and reset to pending automatically the next time the queue is polled, so a crashed workflow does not wedge the table permanently.

If you decide to skip cron

You keep scheduled publishing, the content queue and the one-off task queue, because those are exactly what the visitor-triggered scheduler computes its next-due timestamp from. You keep them at first-visit precision rather than to-the-minute precision.

What you lose is everything that has no visitor to ride on. On a site that goes quiet overnight, a newsletter batch queued at 02:00 waits for the first morning reader. The nightly backup window can be missed entirely. The weekly Search Console history fetch that feeds the Insights report needs a run within its window to advance.

There is a second, unrelated fallback worth not confusing with the scheduler: roughly one front-end request in fifty also nudges the AI bulk-analysis worker along. That one is genuinely probabilistic, it touches only that worker, and it exists so a site without cron still finishes a bulk SEO analysis eventually rather than never.

The honest summary: a hobby blog is fine without cron. Anything with a newsletter, a backup policy or a publishing schedule someone is counting on should have a real cron job.

Be the first to know

New features, release notes and CMS guides. We send a couple of emails a month.