elandlord/nats-php-bundle
A Symfony bundle that integrates NATS with Symfony Messenger using a NatsConnection, helping you publish and consume messages through NATS in Symfony applications.
Architecture fit — the headline finding: this is not a Laravel package. Despite being routed through a PHP package index, elandlord/nats-php-bundle is a Symfony bundle. It has no Laravel service provider, no Illuminate\Contracts\Queue\Queue driver, no queue.php connection type, and no integration with php artisan queue:work, Horizon, or the jobs/failed_jobs tables. It bridges NATS to Symfony Messenger — a different bus, transport, and container abstraction. For a Laravel estate, out-of-the-box architectural fit is effectively zero; the package's 22.4 score reflects opportunity volume in the PHP ecosystem, not fitness for our stack.
What is reusable is the underlying client: elandlord/nats-php is framework-agnostic PHP and is the actual NATS protocol implementation. The bundle is thin Symfony glue on top of it (transport, DI wiring, serializer hydration). That distinction should drive the decision — the valuable, portable asset is the dependency, not the package itself.
Where Symfony-shaped fit does exist: event-driven services needing pub/sub or queue semantics without RabbitMQ/Kafka operational weight, teams already running Symfony Messenger with retry/middleware, and teams practising topology-as-code. None of that describes our Laravel services.
Integration feasibility. Three viable paths, in increasing cost:
composer require elandlord/nats-php, wrap it in an internal NatsPublisher/NatsConsumer adapter, and expose it through Laravel events and an artisan daemon command. Avoids Symfony entirely. Feasible in days, but you own the adapter, ack semantics, and reconnection logic.Option 3 should be rejected on total-cost grounds unless NATS becomes a platform standard. Option 1 is the pragmatic path if NATS is genuinely required.
Technical risk.
| Risk | Assessment |
|---|---|
| Ecosystem mismatch | Highest-order risk. Any adoption means bridging Laravel queues ↔ Symfony Messenger, or writing our own glue. Neither is free. |
| Patch-level behaviour change | v1.4.3 (patch) makes consumer config changes apply to existing durable consumers — previously inert config now reaches production on worker restart. A patch release that mutates production broker topology is a change-management hazard; pin exact versions and treat consumer config as reviewed production config. |
| Pre-1.4.0 correctness | v1.4.0's changelog is literally "fix to ack nats message," and v1.1.0 fixes "many NATS messages are not picked up." Any version below 1.4.x should be treated as having known message-loss/ack defects. |
| Bus factor | Effectively 1–2 maintainers at a single Dutch agency; releases are driven by internal ticket IDs (DEV-5396, DEV-4603). This is a company's internal tool published to Packagist, not a community project. |
| Signal quality | 4 stars, 0 dependents, README is one sentence, no docs site, no visible test suite or CI badge. Zero external production validation to learn from. |
| Serialization coupling | Hydration goes through Symfony Serializer (v1.3.0) with a BackedEnumNormalizer added in v1.4.1. Laravel's serialization conventions differ; payload contracts need explicit definition rather than defaults. |
| Composer surface | Pulling symfony/messenger into a Laravel app means carrying a second DI/container world and a symfony/* version constraint that must survive Laravel major upgrades. |
Key questions.
elandlord/nats-php is abandoned?symfony/messenger as a direct dependency in Laravel apps, or does that violate our dependency policy?Stack fit. Assuming Laravel 10/11/12 on PHP 8.2+ with Redis or SQS queues, Horizon for workers, and Octane in places — none of that speaks Symfony Messenger. The realistic integration surface is: (a) an application-level publisher service, (b) a long-running artisan consumer command managed by Supervisor or Horizon's supervisor, and (c) a mapping layer between Laravel events/jobs and NATS subjects. JetStream is required for any durability; raw NATS core is fire-and-forget and should be treated as such.
Migration path. Recommended, staged:
elandlord/nats-php directly — no bundle. Prove publish, consume, ack-on-success, redelivery-on-failure, and reconnect. Write down what the bundle would have given us and whether we miss it.failed_jobs semantics until a dead-letter equivalent is proven.Compatibility notes if the bundle is nonetheless evaluated. It requires Symfony 6.4+/7.x with symfony/messenger and symfony/serializer. Laravel already depends on a broad set of symfony/* components pinned by major version, so a direct install invites constraint conflicts that will resurface at every Laravel upgrade. Configure NATS as a Messenger transport via framework.messenger.transports with a DSN from env; map subjects to Messenger routes; and record nats consumer info <stream> <durable> before and after any deploy that touches consumer config, because v1.4.3 will reconcile existing durables to code.
Serialization must be specified, not defaulted. Choose JSON with an explicit envelope (message type, version, correlation ID, occurred-at) or Protobuf if cross-language consumers exist. Do not rely on Serializer's type inference for domain objects that will evolve.
Sequencing dependencies. NATS server (or managed NATS) with TLS and auth → JetStream streams and retention policy → subject naming convention → envelope schema → publisher adapter → consumer daemon with graceful shutdown and ack-on-completion → observability (consumer lag, redeliveries, pending messages) → dead-letter handling → then, and only then, migrate anything business-critical.
Maintenance. Assume we own it. The vendor publishes on its own cadence against internal tickets, with no public roadmap, no security policy, and no deprecation policy. Version 1.4.3 landing as a patch with a production-topology behaviour change is direct evidence that semantic-versioning conventions here are not a contract we can plan against. Pin exact versions; read every changelog; never auto-merge.
Support. There is no support channel. Four stars and zero dependents means no Stack Overflow corpus, no blog posts, and no peers who have hit our failure modes. Debugging will be reading source. Budget for that explicitly, and prefer option 1 (core client) so the surface we must read is a library rather than a bundle plus a framework integration.
Scaling. NATS itself scales comfortably; the bottleneck is PHP. Throughput is bounded by worker count × handler latency, and the process-per-worker model means durable consumer concurrency must be tuned against max_ack_pending and the stream's replica/storage config. There is no Horizon integration, so we lose the dashboard, auto-scaling, and job-tagging we rely on today. If Horizon parity matters, that gap alone may be disqualifying. JetStream persistence also introduces disk and retention management that our current Redis/SQS setup does not require — new operational surface for the platform team.
Failure modes to design against.
failed_jobs. Must be built: max-deliver limits, an advisory-driven parking subject, and an operator-facing replay tool.Ramp-up. The team knows Laravel queues; NATS requires new mental models — streams vs. subjects vs. durable consumers, deliver policies, ack policies, ack waits, and the difference between core NATS and JetStream. That is roughly 2–3 weeks to productive, longer to confident. If the bundle path is taken, add Symfony Messenger concepts on top (middleware, stamps, transport serializers) — a second unfamiliar abstraction stacked on the first. Realistic estimate: one engineer, one month, to reach competent operation of a single-service pilot; a quarter before it is safe for business-critical traffic.
Recommendation. Do not adopt this package into a Laravel estate. It solves a Symfony problem we do not have, on a maintenance base too thin to delegate to. If NATS is a genuine platform requirement, run a two-week spike against elandlord/nats-php behind an internal adapter and make the build-vs-buy call on evidence — not on this package's release history.
How can I help you explore Laravel packages today?