ossycodes/laravel-cloudflare-queue

A production-ready Cloudflare Queue driver for Laravel, implementing the full Queue contract.

Maintainers

Package info

github.com/ossycodes/laravel-cloudflare-queue

pkg:composer/ossycodes/laravel-cloudflare-queue

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-05-30 11:11 UTC

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_ms must be large enough to cover batch_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 .