Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Beanstalkd Messenger Laravel Package

symfony/beanstalkd-messenger

Symfony Messenger transport for Beanstalkd. Send and consume messages via Beanstalkd queues using a Messenger-compatible transport with DSN configuration and worker support, ideal for lightweight background job processing in Symfony apps.

Deep Wiki
Context7

Getting Started

First, decide if you actually need it. Laravel already ships a Beanstalkd queue driver ('beanstalkd' => ['driver' => 'beanstalkd', ...] in config/queue.php) built on pda/pheanstalk. If your goal is "push a job, run php artisan queue:work", stop — this package will not help you and will not plug into Illuminate\Queue at all.

symfony/beanstalkd-messenger is a Symfony Messenger transport bridge. It implements Symfony\Component\Messenger\Transport\SenderInterface / ReceiverInterface on top of Beanstalkd (via Pheanstalk). It is useful in a Laravel codebase only when:

  • you have a hybrid Symfony/Laravel monorepo or a shared bus between services,
  • you specifically want Messenger's envelope/stamp/middleware model (routing, retry strategies, failure transports, DelayStamp, custom middleware),
  • or you're consuming a tube that another Symfony service is already writing to.

Minimal install

composer require symfony/messenger symfony/beanstalkd-messenger pda/pheanstalk

Where to look first

  1. Symfony\Component\Messenger\Bridge\Beanstalkd\Transport\BeanstalkdTransportFactory::createTransport() — the authoritative list of DSN query options for the version you installed (tube, timeout, ttr, serializer, etc.). Don't trust blog posts over this method.
  2. BeanstalkdReceiver / BeanstalkdSender — a few hundred lines; read both to understand ack/nack/release behaviour before you build retry logic on top.
  3. Symfony\Component\Messenger\Transport\Transport::fromDsn() — the factory entry point you'll call from a service provider.

First use case: a tiny DTO on a tube

use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Transport\Transport;

$transport = Transport::fromDsn('beanstalkd://127.0.0.1:11300?tube=emails&ttr=120');

$transport->send(new Envelope(new SendWelcomeEmail($userId)));

// Consume (no artisan command exists for this — you write it)
$worker = new \Symfony\Component\Messenger\Worker(
    [$transport],
    $bus,           // Symfony MessageBusInterface
    $logger,
    $eventDispatcher,
    $retryStrategy
);

$worker->run(['time_limit' => 3600, 'memory_limit' => '256M']);

Register the transport, bus, and handlers map as singletons in an AppServiceProvider (or a dedicated MessengerServiceProvider). Laravel's container can hold MessageBusInterface perfectly fine; dispatch() and ShouldQueue will not, however, route to it.

Implementation Patterns

Tube-per-concern, not tube-per-app. Beanstalkd tube names are global on the server. In multi-environment or multi-tenant setups, prefix them (prod-emails, tenant-42-billing) or you'll have staging workers chewing on production jobs. Wire routing explicitly:

$bus = new MessageBus([
    new SendMessageMiddleware(new SendersLocator([
        SendWelcomeEmail::class => ['beanstalkd_emails'],
        SyncInvoice::class     => ['beanstalkd_billing'],
    ], $container)),
    new HandleMessageMiddleware(new HandlersLocator([
        SendWelcomeEmail::class => [new SendWelcomeEmailHandler()],
    ])),
]);

Send IDs, not models. Laravel devs reach for SerializesModels reflexively. Messenger's default PhpSerializer will happily serialize an Eloquent model — and then explode on the consumer when the schema or class changes. Send a small DTO with scalar IDs and reload inside the handler. This also keeps you under Beanstalkd's job-size cap (see Gotchas).

Replace Laravel job features deliberately. You give up ShouldQueue, tries, backoff, retryUntil, failed(), middleware(), and Horizon visibility. Rebuild them with Messenger primitives:

Laravel concept Messenger equivalent
tries / backoff RetryStrategy + SendFailedMessageForRetryListener
failed_jobs table / failed() failure transport (a second Beanstalkd tube or Doctrine transport) + FailedMessageEvent listener
job middleware bus middleware (MiddlewareInterface)
delay() DelayStamp (maps to Beanstalkd put delay)
queue:work + Supervisor custom artisan messenger:consume command + Supervisor
chunked batching BatchHandler / Bus batching, if you're on Symfony 7.x

Run workers as an artisan command. There is no messenger:consume shipped for Laravel (that command lives in symfony/framework-bundle). Write one that builds the Worker, handles SIGTERM (call $worker->stop()), and exits on time_limit/memory_limit. Let Supervisor restart it. Mirror the flags Laravel devs expect (--time-limit, --memory, --tries) so ops doesn't need a new runbook.

Coexistence. You can run Laravel's beanstalkd driver for ordinary jobs and this bridge for the event bus, as long as the tubes never overlap. Use one Supervisor program per tube so a stuck billing consumer doesn't starve emails.

Testing. Swap in in-memory:// or sync:// for unit tests; for integration, send to a real tube and assert with $transport->get() (returns an iterable of envelopes). Pheanstalk's stats-tube is your friend when a test hangs.

Gotchas and Tips

  • TTR is the #1 footgun. Beanstalkd re-releases a reserved job after ttr seconds. If a handler runs longer than TTR, the job is redelivered while still running, giving you duplicate execution. Set ttr well above worst-case handler time, or touch() the job from the handler periodically (Pheanstalk's touch() via a decorated receiver).
  • 64 KB job limit by default. Beanstalkd's -z server flag controls max job size; the stock value is 65535 bytes. A serialized Eloquent model with relations blows past this instantly. Keep payloads as IDs + minimal scalars.
  • Beanstalkd is in-memory. A server restart (or OOM) loses ready, delayed, and buried jobs. Treat this transport as at-most-once for anything you can't afford to lose, or persist a durable record before sending (outbox pattern).
  • Retry loops are silent by default. If a handler throws and no RetryStrategy is configured, the receiver's reject/release behaviour can cycle indefinitely with no failed_jobs row to inspect. Always pair the receiver with a retry strategy and a failure transport, and alert on buried-job count.
  • Serializer mismatch between producer and consumer is the nastiest outage. PhpSerializer requires identical class names and shapes on both ends; JSON serializer drops non-serializable stamps. Pick one explicitly, version your message contracts, and never change a DTO's property names without a rollout plan.
  • Tube names are not namespaced. There is no Laravel-style queue.connections.beanstalkd.queue prefix. ?tube=default in staging and production on one shared server is a real incident waiting to happen.
  • DSN options drift between minor versions. Read BeanstalkdTransportFactory::createTransport() in vendor/ rather than trusting docs; also URL-encode tube names with colons or dashes.
  • Low adoption risk. 19 stars means little community pressure on edge cases. Pin an exact version in composer.json, and keep a thin wrapper interface around the transport so you can swap to symfony/amqp-messenger or Laravel's native queue without rewriting call sites.
  • Debugging toolkit: pheanstalk (or telnet 11300) → list-tubes, stats-tube <name>, peek-ready, peek-buried, kick 10. Buried jobs are this stack's equivalent of failed_jobs, so build a small artisan beanstalkd:inspect command early.
  • Extension points worth knowing: decorate TransportInterface for tracing/metrics, implement a custom TransportFactory if you need Beanstalkd priority (Messenger has no PriorityStamp, so you'll need one), and inject a custom SerializerInterface on the transport to control the wire format.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels