How Zenphoto Handles Background Work

Zenphoto does not ship with a persistent daemon or queue worker. Instead, it performs most of its background processing through HTTP-triggered scripts and a lightweight scheduler that lives inside the zp-core directory. The central piece is zp-core/cron_runner.php, a dispatcher that fires any registered cron tasks when it is requested. Plugins that need periodic work — cacheManager, scheduledpublishing, the comment and image search index refresh, and various notification or digest mailers — hook into this runner and expect it to be called on a regular interval.

On a self-managed server you would point a system cron entry at that runner. On Managed Cloud Shared Hosting you have no root, no crontab shell access of the system kind, and no ability to register a systemd timer. What you do have is the cPanel Jupiter Cron Jobs interface and the DirectAdmin Evolution Cron Jobs panel, both of which schedule commands under your own unprivileged account. That is all Zenphoto needs. The confusion for most users comes from assuming Zenphoto runs these jobs by itself whenever a visitor loads a page. It does not. Without an external trigger, cacheManager never pre-generates thumbnails, scheduledpublishing never flips a future-dated album to published, and stale cache folders under cache/ grow until they consume inodes.

The second source of trouble is the way these scripts are invoked. cron_runner.php can be called either over HTTP (via curl or wget hitting the public URL) or via the PHP CLI binary directly against the file path. Each method behaves differently on CloudLinux. The HTTP method runs under the LiteSpeed PHP handler and respects your domain's .user.ini and PHP Selector settings. The CLI method may use a different PHP version and a different set of limits entirely, which is why a job that works in a browser can silently fail from cron. Understanding which path you are using is the first step to a reliable setup.

Configuring the Cron Trigger in cPanel and DirectAdmin

Begin inside the Zenphoto admin dashboard. Go to Admin → Options → Plugins and confirm that cron_runner is enabled, then open Options → Admin (or the specific plugin tab such as Cache Manager and Scheduled publishing) and note the token or secret some versions require in the request URL. Zenphoto generates the runner URL for you on the plugin page; it typically looks like https://yourdomain.com/zp-core/cron_runner.php?XSRFToken=.... Copy that exact string, because the token guards the endpoint against drive-by execution.

In cPanel Jupiter, open Advanced → Cron Jobs. Under Add New Cron Job, pick a sensible interval. Every fifteen minutes is a reasonable default for a photo gallery; running it every minute wastes CPU seconds you are billed against. Set the fields to */15 * * * * and use an HTTP-based command so the script runs under the same PHP handler as your site:

*/15 * * * * /usr/bin/wget -q -O /dev/null "https://yourdomain.com/zp-core/cron_runner.php?XSRFToken=YOUR_TOKEN"

If wget is restricted on your account, use curl instead, which is almost always available:

*/15 * * * * /usr/bin/curl -s "https://yourdomain.com/zp-core/cron_runner.php?XSRFToken=YOUR_TOKEN" > /dev/null 2>&1

In DirectAdmin Evolution the equivalent lives under Advanced Features → Cron Jobs. The five time fields are separate inputs rather than a single line, but the command box takes the same curl string. DirectAdmin will email cron output to the account address unless you redirect it, which is why the > /dev/null 2>&1 suffix matters — without it you receive a message every fifteen minutes.

If you must run the CLI variant — for example when the plugin page offers a cli option or when HTTP times out on a very large scan — select the correct interpreter. CloudLinux exposes per-version binaries such as /opt/alt/php82/usr/bin/php. Do not call plain php, which may resolve to an old default. A CLI job looks like this:

*/15 * * * * /opt/alt/php82/usr/bin/php -q /home/username/public_html/zp-core/cron_runner.php

Replace username and the PHP version with values from your account. You can confirm the active version in cPanel under MultiPHP Manager or in DirectAdmin's Select PHP Version selector, and match the CLI path to it so both the browser and cron use identical extensions and settings.

Tuning PHP Limits for Long-Running Jobs

The most common failure is a job that starts, chews through a few hundred thumbnails, and dies halfway. That is almost never a Zenphoto bug — it is the HTTP request hitting max_execution_time or memory_limit. Because the cron runner uses the LiteSpeed PHP handler when triggered over HTTP, you control those ceilings through a .user.ini file in your document root, not through php.ini or any file under /etc, which you cannot touch.

Create or edit public_html/.user.ini using the cPanel or DirectAdmin File Manager and raise the values Zenphoto needs for large cache builds:

max_execution_time = 300
max_input_time = 300
memory_limit = 256M
post_max_size = 64M
upload_max_filesize = 64M

LiteSpeed caches .user.ini for a few minutes, so changes are not instant. You can verify the applied values by temporarily placing a phpinfo() page in the document root, or by checking Select PHP Version → Options in DirectAdmin, which reads the same directives through the PHP Selector. Keep max_execution_time realistic. Shared hosting enforces an upstream LiteSpeed timeout independent of PHP, often around 120–300 seconds, so a request that needs ten minutes will be killed by the web server regardless of what PHP allows. For very large initial thumbnail generation, split the work: let cacheManager run its incremental batches across several cron invocations rather than forcing one marathon request. Each run processes a chunk, writes progress, and the next scheduled run resumes.

Memory matters just as much for galleries with high-resolution originals. Resizing a 40-megapixel JPEG through GD can briefly require well over 128 MB. If the job dies without an obvious error, raise memory_limit in .user.ini and re-run. If your account caps are lower than the values above, the hosting enforcement wins and the directive is clamped silently — that limit is set at the CloudLinux LVE level and is not something you edit from inside the account.

Troubleshooting Silent and Stalled Jobs

When a scheduled task appears to do nothing, work through the evidence in order. First, confirm the cron fired at all. Temporarily remove the output redirection so cPanel or DirectAdmin emails you the result, or redirect to a log you can read: append >> /home/username/cron_zp.log 2>&1 to the command. After one interval, open that file in File Manager. An empty file means the runner ran cleanly; HTML output means the token is wrong or the URL redirected to a login page; a curl: (6) or (7) means DNS or connection trouble, usually from using http where the site forces https via .htaccess.

Second, check Zenphoto's own log. The admin dashboard exposes it under Admin → Overview → Logs, and the same entries land in cache/logs/ or the debug log depending on version. Look for lines written at your cron timestamp. If scheduledpublishing runs but items stay unpublished, verify the publish date is actually in the past and that the album's parent is itself published — a future-dated parent keeps children hidden no matter what the runner does.

Third, inspect the account error_log. LiteSpeed writes a per-directory error_log next to the script that faulted, so check both public_html/error_log and public_html/zp-core/error_log. Fatal messages about exhausted memory or exceeded execution time point straight back to the .user.ini tuning in the previous section. A 500 at the runner URL with no PHP fatal usually indicates a .htaccess rule blocking direct access to zp-core; if you have hardened that folder, add an exception so cron_runner.php remains reachable:

<Files "cron_runner.php">
  Require all granted
</Files>

Finally, watch your resource usage. A gallery that triggers full cache rebuilds every minute can trip the CloudLinux CPU and entry-process limits visible in cPanel's Resource Usage graph, causing the very 508 and timeout errors that look like broken cron. Widen the interval, let incremental batches do the work, and keep the cache/ directory pruned so inode counts stay healthy. The diagnostic method here mirrors the broader approach in our Piwigo error diagnosis guide, since both galleries surface background failures through the same PHP and LiteSpeed logs. Read the logs first, confirm the trigger fired, then adjust limits — in that sequence you resolve nearly every stalled Zenphoto job without ever needing root.