Jobs & Scheduling

Long-running operations run as Laravel queue jobs. The scheduler in routes/console.php triggers backup, healthcheck, update, integrity, metrics, and domain-whois commands on a cron.

Queues

Two Redis queues, two supervisor programs (docs/DEPLOYMENT.md, "Queue Worker Setup"):

QueueWorkersCarries
defaultwpworks-queue ×2, --queue=defaultSeconds-long, latency-sensitive jobs: WP-Cron, healthchecks, cache purges, provisioning, certificates, DNS, SFTP.
heavywpworks-queue-heavy ×1, --queue=heavy,default --timeout=2700Minutes-long jobs: backups, updates, clones, restores, integrity checks. Never blocks default; drains it when idle.

A job picks its queue in its constructor with $this->onQueue(self::QUEUE), so every dispatch path is covered; tests/Feature/QueueRoutingTest.php guards the routing. Depths of both queues are shown on the Capacity & insights page, and wphoster:monitor-queues raises a queue_backlog alert when default stays above 200 waiting jobs for 10 minutes.

Queue jobs

JobQueuePathPurpose
ProvisionSiteJobdefaultapp/Jobs/ProvisionSiteJob.phpProvisions new site via Provisioner
CloneSiteJobheavyapp/Jobs/CloneSiteJob.phpClones site via CloneEngine
BackupSiteJobheavyapp/Jobs/BackupSiteJob.phpCreates DB or uploads backup
RestoreSiteJobheavyapp/Jobs/RestoreSiteJob.phpRestores from Backup
RunUpdateJobheavyapp/Jobs/RunUpdateJob.phpRuns UpdateService for site(s)
IntegrityCheckJobheavyapp/Jobs/IntegrityCheckJob.phpRuns IntegrityCheckService for a site
RunWpCronJobdefaultapp/Jobs/RunWpCronJob.phpwp cron event run --due-now for one site (sites have DISABLE_WP_CRON)
RunHealthcheckJobdefaultapp/Jobs/RunHealthcheckJob.phpRuns HealthcheckService for site(s)
PurgeSiteCacheJobdefaultapp/Jobs/PurgeSiteCacheJob.phpPurges page cache + nginx + Cloudflare, warms origin
IssueCertificateJobdefaultapp/Jobs/IssueCertificateJob.phpLet's Encrypt certificate for a customer domain
SyncCloudflareDnsJobdefaultapp/Jobs/SyncCloudflareDnsJob.phpSyncs Cloudflare A records
SyncSftpUserJobdefaultapp/Jobs/SyncSftpUserJob.phpCreates/updates/removes an SFTP user and jail
CheckDomainWhoisJobdefaultapp/Jobs/CheckDomainWhoisJob.phpDomain WHOIS checks (if used)

Schedule (routes/console.php)

CommandSchedule
wphoster:run-backups dbDaily at 02:00
wphoster:run-backups uploadsDaily at 03:00
wphoster:purge-expired-backupsDaily at 04:00
wphoster:run-healthchecksEvery 10 minutes
wphoster:run-wp-cronEvery wphoster.wp_cron.interval_minutes minutes (default 2, */2 * * * *), withoutOverlapping
wphoster:run-wp-cron --every-minute-sitesThe minutes in between, only for sites with "Run WP-Cron every minute" (metadata.wp_cron.every_minute)
wphoster:monitor-queuesEvery minute
wphoster:sync-sftpEvery 5 minutes
wphoster:run-updates --staging-firstDaily at 01:00
wphoster:rebuild-file-manifest --all --only-missingDaily at 05:45
wphoster:run-integrity-checksDaily at 06:00
wphoster:collect-metricsHourly (disk/DB + php-fpm max_children hits per site)
wphoster:check-domains-whoisDaily at 05:00
wphoster:send-renewal-remindersMondays at 09:00

WP-Cron interval

WordPress core and plugin cron events are hourly, twice-daily or daily, and WordPress itself only runs them "on the next page load after they are due", so a 2-minute grain changes nothing visible while halving the ~17k jobs/day that one-minute ticks produced on 13 sites. Shops that rely on one-off timers (order-email retries, abandoned-cart reminders, Action Scheduler) can opt into every-minute runs on their edit page. Change the fleet interval with WP_HOSTER_WP_CRON_INTERVAL_MINUTES; above 5 minutes busy shops start to fall behind.

Artisan commands

Commands live under app/Console/Commands/. Key ones:

Ensure the scheduler runs (e.g. php artisan schedule:work in dev or cron * * * * * php /path/artisan schedule:run in production) and queue workers are running (in development one php artisan queue:work --queue=heavy,default covers both queues; in production see docs/DEPLOYMENT.md).