A production-ready Cloudflare Queue driver for Laravel that implements the full Queue contract, fixing all known issues with existing community packages.
- PHP 8.1+
- Laravel 10, 11, or 12
- Cloudflare account with a Queue created
- Cloudflare API token with Queues Read and Queues Write permissions
composer require ossycodes/laravel-cloudflare-queueThe service provider is auto-discovered via Laravel's package discovery.
Add a connection entry to config/queue.php:
'connections' => [
'cloudflare' => [
'driver' => 'cloudflare',
'account_id' => env('CLOUDFLARE_ACCOUNT_ID'),
'queue_id' => env('CLOUDFLARE_QUEUE_ID'),
'api_token' => env('CLOUDFLARE_API_TOKEN'),
// Number of messages to pull per poll cycle (default: 1)
// Increase this to reduce API calls when processing at volume.
// Set visibility_timeout_ms high enough to cover:
// batch_size × average job processing time
'batch_size' => env('CLOUDFLARE_QUEUE_BATCH_SIZE', 1),
// How long a pulled message is hidden from other consumers (ms, default: 30000)
// Must be > batch_size × average processing time to prevent duplicate processing.
'visibility_timeout_ms' => env('CLOUDFLARE_QUEUE_VISIBILITY_TIMEOUT_MS', 30000),
// Dispatch jobs after the current database transaction commits (default: false)
'after_commit' => false,
// Optional: class to handle raw messages pushed by a Cloudflare Worker
// (not by Laravel's own Queue::push). Leave null for standard Laravel jobs.
'raw_handler' => null,
],
],Add the corresponding .env variables:
CLOUDFLARE_ACCOUNT_ID=your-account-id
CLOUDFLARE_QUEUE_ID=your-queue-id
CLOUDFLARE_API_TOKEN=your-api-tokenSet as the default queue driver:
QUEUE_CONNECTION=cloudflareWorks exactly like any other Laravel queue driver:
// Dispatch immediately
MyJob::dispatch($data);
// Dispatch with a delay
MyJob::dispatch($data)->delay(now()->addMinutes(5));
// Dispatch multiple jobs
Queue::bulk([new JobA(), new JobB()]);php artisan queue:work cloudflare
php artisan queue:work cloudflare --sleep=3 --tries=3Set batch_size > 1 to pull multiple messages per API call. The driver buffers pulled messages in memory and returns them one-by-one to Laravel's worker loop — so the API is only called when the buffer is empty, not on every job.
// config/queue.php
'cloudflare' => [
'batch_size' => 10,
'visibility_timeout_ms' => 60_000, // 60s — must cover 10 × avg job time
],Important:
visibility_timeout_msmust be large enough to coverbatch_size × average_job_processing_time. If jobs in the buffer expire before they're processed, Cloudflare will make them visible again, causing duplicate processing.
If you push messages from a Cloudflare Worker (not from Laravel), the message body won't follow Laravel's job serialization format. Use raw_handler to route these messages to a specific class:
Cloudflare Worker (producer):
await env.MY_QUEUE.send({ email_to: "user@example.com", subject: "Hello" });Laravel config:
'cloudflare' => [
'raw_handler' => App\Jobs\HandleWorkerEmail::class,
],Handler class:
class HandleWorkerEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable;
public function __construct(public readonly array $data) {}
public function handle(): void
{
// $this->data = ['email_to' => 'user@example.com', 'subject' => 'Hello']
Mail::to($this->data['email_to'])->send(new MyMail($this->data));
}
}When after_commit is true, jobs dispatched inside a database transaction won't be placed on the queue until the transaction successfully commits. This prevents a race where the worker picks up the job before the DB write is visible.
'after_commit' => true,The Cloudflare Queues REST API does not expose a purge endpoint for consumers. Calling Queue::clear() or php artisan queue:clear cloudflare will throw a RuntimeException.
To clear a queue, use:
- The Cloudflare Dashboard → Queues → select queue → Purge
- Or the Wrangler CLI:
wrangler queues purge <queue-name>
Cloudflare Queues does not expose individual job listings via the REST API. The following methods return empty collections (same behaviour as SQS and Beanstalkd):
pendingJobs(),delayedJobs(),reservedJobs()allPendingJobs(),allDelayedJobs(),allReservedJobs()
pendingSize() returns the total message backlog count from the queue metadata.
delayedSize() and reservedSize() return 0.
Laravel Horizon is not fully supported.
Cloudflare Queues supports delay_seconds up to the queue's maximum delay (currently 12 hours). Delayed jobs work via ->delay() or Queue::later().
Unlike Redis and Beanstalkd, Cloudflare Queues does not support long-polling. The worker will poll every --sleep seconds when the queue is empty.
composer install
./vendor/bin/pest
./vendor/bin/pest --coverageMIT
Please buy me a cup of coffee https://www.paypal.com/paypalme/osaigbovoemmanuel , Leave a star and follow me on Twitter .