Zodiac Tech CRM Docs

Queues, jobs & scheduler

The complete scheduled-task inventory, the queue configuration for local and production, and how failures surface.

developer Medium ⏱ 12 min Updated 2026-10-05

Queues, jobs & scheduler

Almost everything "automatic" in the product — WhatsApp appointment reminders, follow-up sweeps, Meta lead health, billing lifecycle, retention, backups — rides on two mechanisms: queued jobs and the Laravel scheduler. The entire schedule is declared in routes/console.php; this page transcribes it in full.

Who this is for

Hosting admins and developers who must configure cron/queues and reason about background behavior.

Difficulty: Medium · Time: about 12 minutes.

Queue configuration

Environment QUEUE_CONNECTION What runs the queue
Local XAMPP (default in .env) sync Nothing — jobs run inline during the request
Production (recommended) database A long-lived worker: php artisan queue:work
  • With sync, deferred behaviors still work because the product is sweep-based: checks run on a schedule and query state, rather than relying on delayed jobs being processed at a precise moment.
  • Production worker (one is enough on a small cPanel install; keep it alive via cron or a process manager):
    php artisan queue:work --tries=3 --timeout=120
    
  • Failed jobs land in failed_jobs (Laravel default). Inspect (php artisan queue:failed), retry (php artisan queue:retry all), or flush (php artisan queue:flush).
  • Jobs may target a named queue; for example push delivery (SendPushToUser) runs on the notifications queue with 2 tries and 30/120-second backoff.

Scheduler setup

One cron entry drives everything:

* * * * * cd /path/to/app && php artisan schedule:run >> /dev/null 2>&1

Every task below runs inside Laravel's scheduler, so a single missed cron minute only delays tasks, and withoutOverlapping prevents pile-ups.

The complete schedule (routes/console.php)

All entries use ->withoutOverlapping() (10 minutes unless noted) and ->onFailure(...) logging a structured schedule task failed: <name> error.

# Task Cadence Purpose
1 CheckImapMailboxes every 5 min Poll connected mailboxes for inbound email
2 CheckNoActivityAutomations hourly Fire no-activity automations
3 CheckAppointmentApproaching every 5 min WhatsApp appointment reminders inside each tenant's hours_before window (dedupe key prevents repeats)
4 CheckFollowUpDue every 5 min Fire follow_up_due automations for overdue lead tasks
5 CheckDateFieldAutomations daily 07:00 Birthday/anniversary greetings from date custom fields
6 ProcessEmailSequences every 15 min Advance email sequence enrollments
7 ProcessAiSdrEnrollments every 15 min One AI SDR decision/action per due enrollment (no-op in demo mode)
8 notifications:digest hourly Send notification digests
9 tasks:send-reminders every 5 min Notify assignees of due tasks (separate from #4, which drives automations)
10 leadhub:meta-health every 6 h Ping stored Meta page tokens so "connected but expired" surfaces early
11 leadhub:sync-meta-ads --days=30 daily 04:30 Pull campaign/ad/insights data; skips tenants without credentials
12 leadhub:purge-recordings daily 03:15 30-day recording retention (files deleted, metadata row marked expired)
13 leadhub:meta-ops-scan every 30 min Needs-attention scan (no delivery / zero leads / CPL spikes / paused / dead connections)
14 leadhub:meta-ops-brief daily 07:15 Daily internal ads brief, after the nightly sync
15 reports:deliver hourly Deliver scheduled reports
16 filters:send-alerts hourly Saved-filter alert emails
17 leadhub:subscription-lifecycle daily 06:00 Commercial subscription lifecycle transitions
18 invoices:process-recurring daily 07:00 Generate recurring invoices; due-date reminders; mark overdue
19 leadhub:capture-benchmarks daily 03:30 Capture metric benchmarks
20 leadhub:sync-calendars every 15 min Calendar sync
21 leadhub:purge-audit-logs daily 03:00 Audit 180 d / login attempts 30 d / processed billing events 90 d (env-overridable)
22 leadhub:execute-erasure daily 04:00 GDPR Art. 17 hard-delete after the 30-day cool-off
23 leadhub:backup daily 02:00 Nightly backup — guarded by GeneralSettings::auto_nightly_backup (or leadhub.backups.auto_nightly)
24 CreateRenewalInvoice daily 06:30 Create renewal invoices for active schedules
25 CheckUpcomingRenewals daily 07:00 Renewal reminders
26 CheckPaymentFailures hourly Detect and react to failed payments
27 ApplyGracePeriod daily 07:15 Start grace period (failure ladder step 1)
28 ApplyRestriction daily 07:30 Apply restriction (failure ladder step 2)
29 ReconcileProviderStatus every 30 min Reconcile subscription state with the payment provider
30 SyncAppliedChangeRequests every 30 min Apply approved billing change requests
31 DetectStuckOnboarding daily 08:15 Flag client onboarding steps unsettled > 24 h, notify SA (once/day/step)

No phone-home The former daily update check and nightly license re-check were removed. Nothing in this scheduler makes outbound calls to the vendor's servers.

Failure handling

  • ->withoutOverlapping(10) means a stuck run releases the slot after 10 minutes (production-critical syncs use 20–30).
  • ->onFailure($logFailure('Name')) writes schedule task failed: Name to the log, which is greppable for alerting — a generic "Exception in cron" no longer hides which task broke.
  • Long-running data work (Meta sync, recording purge, Meta ops) uses larger overlap windows deliberately.

Was this page helpful?