πŸ›’ Bringo

Website & Admin Panel

Cron & Queue Workers

Two background processes keep the platform running smoothly: the scheduler (timed tasks) and the queue worker (jobs like push notifications and emails). Set up both.

⚠️
Without these, the store still takes orders β€” but push notifications, scheduled campaigns, cashback, loyalty points, affiliate payouts and subscription renewals won't run.

1. The scheduler (cron)

Add a single cron entry that runs every minute. Laravel decides what actually needs to run.

🐘
Spell out the full path to PHP β€” do not write plain php. Cron does not read your shell profile, so it gets the system default, which on shared hosting is often an older version than your site uses. Combined with >> /dev/null below, a wrong binary fails silently, forever β€” the classic "my scheduled tasks never ran and nothing was logged" case.

Find the right binary and the app's real path first:

cd /path/to/your/app && pwd          # the exact app path
ls /usr/bin/php8* /opt/alt/php8*/usr/bin/php 2>/dev/null   # available binaries
which php                                # what plain "php" would give cron

Then build the entry from those two values:

# shared hosting (CloudLinux / Hostinger layout)
* * * * * cd /home/USER/domains/YOURDOMAIN/public_html/shop && /opt/alt/php84/usr/bin/php artisan schedule:run >> storage/logs/cron.log 2>&1

# cPanel with a system PHP
* * * * * cd /home/USER/bringo && /usr/local/bin/php artisan schedule:run >> storage/logs/cron.log 2>&1

# VPS
* * * * * cd /var/www/bringo && /usr/bin/php artisan schedule:run >> storage/logs/cron.log 2>&1
πŸ”Ž
Note these log to storage/logs/cron.log rather than /dev/null. Leave it that way for the first day: after a few minutes tail storage/logs/cron.log tells you whether cron is running at all and whether it found PHP. Once you have seen it work, switch the tail to >> /dev/null 2>&1 if you would rather not grow a log file β€” and remember to clear the log occasionally if you keep it.

Verify it by hand before trusting cron β€” run the exact command from the entry, with the full binary path:

cd /path/to/your/app && /opt/alt/php84/usr/bin/php artisan schedule:run

You should see either a list of tasks that ran, or "No scheduled commands are ready to run." Anything else β€” a PHP version error, a missing vendor/ β€” would have been invisible inside cron.

On cPanel/hPanel, add the entry through Cron Jobs in the panel rather than crontab -e.

What the scheduler runs

TaskScheduleWhat it does
notifications:dispatch-scheduledEvery minuteSends admin notification campaigns that are due.
partner:expire-offersEvery minuteExpires delivery-assignment offers no rider answered.
food:auto-acceptEvery minuteAuto-accepts food orders whose seller auto-accept timer has elapsed.
accounts:process-deletionsHourlyProcesses customer account-deletion requests that have become due.
cashback:process-seasonalHourlyCredits pending seasonal-campaign cashback for delivered orders and releases reservations of cancelled orders. Until this runs, customers see the amount as β€œPending”.
loyalty:tickHourlyCredits matured loyalty points and expires old ones.
price-alerts:scanHourlyNotifies customers when a wishlisted product drops in price.
cashback:process-monthlyDaily 02:15On your configured day-of-month, sums last month's spend per customer and credits monthly cashback.
cashback:expireDaily 02:30Expires cashback that passed its expiry date.
wallet:expire-stale-topup-intentsDaily 03:00Cleans up abandoned wallet top-up payments.
subscriptions:processDaily 05:00Generates the day's subscription orders.
affiliates:creditDaily 06:00Credits matured affiliate commissions.
partner:document-expiry-remindersDaily 08:30Reminds delivery partners about expiring documents.

2. Triggering tasks over HTTP (no shell access needed)

Most scheduled tasks can also be fired through a URL β€” useful on shared hosting without cron access, with external cron services (cron-job.org, EasyCron…), or for testing a task right now. The single /cron/run URL runs the whole schedule, so it covers every task including the ones with no individual URL.

Set the secret key

Add a long random secret to your .env, then refresh the config cache:

CRON_SECRET=paste-a-long-random-string-here
php artisan config:clear && php artisan config:cache
πŸ”’
Treat CRON_SECRET like a password. Anyone who has it can trigger these tasks. Requests with a wrong or missing key get 403.

URL format

https://your-domain.com/cron/run/{task}?key=CRON_SECRET

The most important URL β€” run everything that is due (point your external cron service at this one, every minute or every 5 minutes):

https://your-domain.com/cron/run?key=CRON_SECRET

All task URLs

Replace your-domain.com and CRON_SECRET with your values. Prefix each with https://your-domain.com/cron/run/:

URLOptional parameters
cashback:process-seasonal?key=…—
cashback:process-monthly?key=…&force=1 run before the scheduled day Β· &month=2026-06 settle a specific month (YYYY-MM)
cashback:expire?key=…—
loyalty:tick?key=…&credit=1 or &expire=1 to run only one half
subscriptions:process?key=…&date=2026-07-21 process a specific day
affiliates:credit?key=…&date=2026-07-21
notifications:dispatch-scheduled?key=…—
wallet:expire-stale-topup-intents?key=…&hours=12 override the staleness window
partner:expire-offers?key=…—
partner:document-expiry-reminders?key=…&date=2026-07-21
price-alerts:scan?key=…—

Example β€” settle seasonal cashback immediately instead of waiting for the hourly run:

https://your-domain.com/cron/run/cashback:process-seasonal?key=CRON_SECRET
ℹ️
Only the tasks listed above are allowed through this URL β€” anything else (including food:auto-accept and accounts:process-deletions, which run through the full schedule instead) returns 404. Each response reports the exit code and runtime, so your cron service can alert on failures.
πŸ”
You can send the secret as an X-Cron-Key header instead of ?key=. Prefer the header where your cron service supports it β€” a key in the query string ends up in Referer headers, browser history and proxy logs.

3. The queue worker

Jobs (push notifications, emails) are pushed to a queue and processed by a worker.

On a VPS β€” use Supervisor (recommended)

sudo apt install -y supervisor

Create /etc/supervisor/conf.d/bringo-worker.conf:

[program:bringo-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/bringo/artisan queue:work database --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
user=www-data
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/bringo/storage/logs/worker.log
stopwaitsecs=3600
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start bringo-worker:*

On shared hosting β€” cron fallback

If you can't run a permanent process, add a second cron job that drains the queue periodically. The same rule applies β€” full path to PHP, real app path:

* * * * * cd /home/USER/domains/YOURDOMAIN/public_html/shop && /opt/alt/php84/usr/bin/php artisan queue:work --stop-when-empty >> storage/logs/worker.log 2>&1

--stop-when-empty makes the worker exit once the queue is drained, so the next minute's run starts cleanly instead of stacking up processes. If your host limits how long a process may live, add --max-time=50 so it always finishes inside the minute.

♻️
After deploying new code, restart the worker so it loads the latest version: php artisan queue:restart (Supervisor will respawn it).
πŸ‘‰
Next: the Go-Live Checklist.