How the SuiteCRM Scheduler Actually Works
Nearly every automated action inside SuiteCRM flows through one component: the Scheduler. Inbound email polling, outbound campaign sending, workflow evaluation, report caching, bounce handling, and the AOD index rebuild are all registered as scheduler jobs. None of them run on their own. They are triggered by a single PHP file, cron.php, which must be executed on a repeating interval by the server's cron daemon. When that external trigger is missing or misconfigured, the admin panel will happily show jobs marked as "Active" while nothing ever fires. This is the most common reason a SuiteCRM install appears to "forget" to send campaign emails or import mailboxes.
Each time cron.php runs, SuiteCRM reads the schedulers table, compares each job's interval against its last_run timestamp, and executes any job that is due. The interval field uses a compressed cron expression stored in the job_interval column, for example *::*::*::*::* for "every time cron runs." Because the job frequency is capped by how often the operating system calls cron.php, setting a job to run every minute inside the AdminCP is meaningless unless the server-side cron also runs every minute. On shared hosting you control that interval yourself through cPanel or DirectAdmin, since you have no access to the system crontab or root.
There is one hard requirement that trips up most new installs. The command line user that executes cron.php must match the web server user, or SuiteCRM refuses to run and logs a message about running as the wrong user. On LiteSpeed with CloudLinux, PHP already runs as your cPanel/DirectAdmin account user, so invoking the job with the account's own PHP binary satisfies this check automatically. The failures usually come from calling cron.php through a browser URL (which does not establish a true CLI session) or from pointing the cron at the wrong PHP version.
Configuring the Cron Job in cPanel and DirectAdmin
Start by confirming the absolute path to your SuiteCRM document root. For a primary domain this is normally /home/youruser/public_html; for an addon domain or subdomain it will be a path like /home/youruser/crm.example.com. You can verify it in the File Manager address bar. You also need the correct PHP binary for the version SuiteCRM is running on. In cPanel, the account-aware wrappers live at paths such as /usr/local/bin/ea-php81, while CloudLinux PHP Selector exposes /usr/bin/php that respects your selected alt-php version. Matching the CLI PHP version to the version set in MultiPHP Manager prevents extension mismatches that cause fatal errors only on the command line.
In cPanel Jupiter, open Advanced > Cron Jobs. Under "Add New Cron Job," set the schedule to run every minute, which SuiteCRM expects. Use the Common Settings dropdown or enter the fields manually as * * * * *. For the command, call PHP directly against the scheduler entry point:
cd /home/youruser/public_html; /usr/local/bin/ea-php81 -f cron.php > /dev/null 2>&1The cd first is important. SuiteCRM resolves config.php and its include paths relative to the working directory, so running php -f /home/youruser/public_html/cron.php from elsewhere can fail to bootstrap. Redirecting output to /dev/null keeps your inbox from filling with cron mail on every run; while debugging, temporarily remove that redirect so any PHP errors land in the cron email instead.
In DirectAdmin Evolution, the equivalent screen is Advanced Features > Cron Jobs. The field layout is identical five-field cron. Enter * in each of the minute, hour, day, month, and weekday boxes, then supply the same command string. If your account uses CloudLinux PHP Selector, the cleaner binary reference is:
cd /home/youruser/domains/example.com/public_html; /usr/local/bin/php -d max_execution_time=0 cron.php >/dev/null 2>&1The -d max_execution_time=0 flag lets long jobs such as campaign sending or an AOD reindex run past the default CLI time limit without being killed mid-batch. CLI PHP normally ignores the web max_execution_time, but setting it explicitly removes any ambiguity introduced by custom host defaults.
After saving, wait two or three minutes, then open Admin > Scheduler in SuiteCRM. The "Last Successful Run" column should advance. If every job still reads "None," the cron is not reaching PHP correctly, and the next section covers how to find out why.
Diagnosing Stuck and Silently Failing Jobs
When jobs refuse to advance, resist the urge to keep editing the cron string and instead gather evidence. Edit the cron command temporarily to drop the >/dev/null 2>&1 redirect so output is emailed to your account, or append >> /home/youruser/suitecrm_cron.log 2>&1 to capture both stdout and stderr in a file you can read through File Manager. A single run will usually reveal the real cause: a wrong-user warning, a database connection error, or a PHP fatal about a missing extension like imap for inbound email.
SuiteCRM's own log is the second place to look. Open suitecrm.log (older installs name it sugarcrm.log) in the application root via File Manager. Raise the logging level under Admin > System Settings > Logger Settings to DEBUG while investigating, then lower it back to FATAL afterward so the file does not grow unbounded on a shared account. Look for entries timestamped to your cron interval. A job that starts but never finishes often shows a memory exhaustion message; in that case raise the CLI memory ceiling by adding memory_limit = 512M to a .user.ini file in the document root, which both the web and CLI PHP handlers on CloudLinux respect.
A classic stuck state is a job frozen in "Job queue" with status running. SuiteCRM uses the job_queue table to track execution, and if a run is killed mid-task (a timeout, a hitting of the account's process limit), the row never flips back to done. The scheduler then assumes the job is still active and skips it forever. Clear these safely in phpMyAdmin by selecting your SuiteCRM database and running a targeted update rather than deleting rows:
UPDATE job_queue SET status = 'done', resolution = 'failure' WHERE status = 'running' AND date_entered < NOW() - INTERVAL 1 HOUR;This releases the stale locks without touching legitimately active jobs. Afterward, confirm the related scheduler's last_run value in the schedulers table looks sane; a last_run stuck in the future (from a server clock change) will also block execution. Inbound email jobs deserve a separate check, because they fail quietly when the IMAP extension is not enabled for your selected PHP version. Confirm it under cPanel's Select PHP Version > Extensions or DirectAdmin's PHP Selector, enable imap, and re-test from the Inbound Email monitor rather than the Scheduler alone. With the trigger firing every minute, matching PHP versions across web and CLI, and the queue cleared of orphaned rows, the Scheduler returns to driving campaigns, workflows, and mailbox polling on schedule.