ossycodes / laravel-cloudflare-queue
A production-ready Cloudflare Queue driver for Laravel, implementing the full Queue contract.
Package info
github.com/ossycodes/laravel-cloudflare-queue
pkg:composer/ossycodes/laravel-cloudflare-queue
Requires
- php: ^8.1
- laravel/framework: ^10.0|^11.0|^12.0
Requires (Dev)
- mockery/mockery: ^1.5
- orchestra/testbench: ^8.0|^9.0|^10.0
- pestphp/pest: ^2.0|^3.0
- pestphp/pest-plugin-laravel: ^2.0|^3.0
This package is auto-updated.
Last update: 2026-05-30 11:51:44 UTC
README
A production-ready Cloudflare Queue driver for Laravel that implements the full Queue contract, fixing all known issues with existing community packages.
Requirements
- PHP 8.1+
- Laravel 10, 11, or 12
- Cloudflare account with a Queue created
- Cloudflare API token with Queues Read and Queues Write permissions
Installation
composer require ossycodes/laravel-cloudflare-queue
The service provider is auto-discovered via Laravel's package discovery.
Configuration
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-token
Set as the default queue driver:
QUEUE_CONNECTION=cloudflare
Usage
Dispatching Standard Laravel Jobs
Works 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()]);
Processing Jobs
php artisan queue:work cloudflare php artisan queue:work cloudflare --sleep=3 --tries=3
Batch Processing (Reducing API Calls)
Set 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.
Handling Raw Messages from a Cloudflare Worker
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)); } }
after_commit
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,
Known Limitations
Queue Clearing Not Supported
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>
Job Inspection (Laravel Horizon)
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.
Delayed Jobs
Cloudflare Queues supports delay_seconds up to the queue's maximum delay (currently 12 hours). Delayed jobs work via ->delay() or Queue::later().
No Blocking Pop
Unlike Redis and Beanstalkd, Cloudflare Queues does not support long-polling. The worker will poll every --sleep seconds when the queue is empty.
Running Tests
composer install ./vendor/bin/pest ./vendor/bin/pest --coverage
License
MIT
How do I say Thank you?
Please buy me a cup of coffee https://www.paypal.com/paypalme/osaigbovoemmanuel , Leave a star and follow me on Twitter .