barryvdh/laravel-async-queue
Laravel Async Queue adds an async queue driver that runs queued jobs in background PHP processes instead of requiring a long-running queue worker. Useful on shared hosting or simple deployments where daemons aren’t available, while keeping Laravel’s queue API.
What a TPM is actually adopting. barryvdh/laravel-async-queue registers an async queue driver with Laravel's QueueManager. On dispatch() it spawns a short-lived background PHP process (via spatie/async, wrapping Symfony Process) that runs a single-job worker invocation, rather than requiring a supervised, long-running queue:work daemon. The program-level framing matters: this is an operational substitution, not an architectural change. dispatch() call sites and ShouldQueue job classes are unchanged. That is what makes adoption cheap — and what makes the exit cheap too, which is the whole reason a TPM should be comfortable with it.
Architecture fit. Clean at the framework seam; it is a driver, so it is a config-level surface area, not a code-level one. It fits small-to-medium Laravel monoliths on a single VPS, bare metal, or shared hosting, with short jobs (sub-30s, ideally sub-5s), low-to-moderate volume, and no dedicated ops capacity. It does not fit anything already running Horizon or a managed broker, Kubernetes, serverless/ephemeral runtimes, high-throughput pipelines, or any workload whose contract includes delivery guarantees. From a program standpoint: adopting this where a broker already exists adds risk for zero delivery benefit, which is the kind of decision that gets flagged in a design review and should be stopped early.
Integration feasibility. High — Composer install, provider registration, a connections entry, and a backing store. Realistically under a day per environment. But that estimate is gated on two platform conditions that are not package concerns and must be proven before the estimate is committed to: (1) the production image permits proc_open, and (2) a PHP CLI binary is discoverable at a known path. Hardened hosts disable the former, and several managed/serverless images lack the latter. Those failures surface at runtime, not install time — which is precisely the class of dependency that a TPM should place behind an explicit gate rather than letting it slip into a sprint estimate.
Technical risk.
0.8.1; do not use 0.8.0. v0.8.1's sole change is "Fix deferred service provider" — lazy registration of the provider and queue connector. On 0.8.0 the driver binding can fail to resolve depending on when the queue connection is first touched relative to provider boot. That class of bug hides under Octane, in test suites that resolve connections early, and in partially-booted console commands. The fix is unglamorous boot-overhead hygiene with no API or config change for job authors — a good sign — but it also confirms boot-order bugs are a live category on the 0.8 line.failed_jobs expose, and dispatch() looks successful whether or not a process spawned.Key questions.
proc_open + PHP CLI discovery been verified on the production image, not just locally?Stack fit. Laravel on PHP 8.x, conventional queue-consuming model, single host or shared platform without process supervision. Mismatch with distributed/high-throughput/serverless deployments, and with organizations already standardized on a broker — consistency beats convenience whenever there is an existing owner and runbook.
Migration path (phased, with gates and owners).
proc_open, CLI discovery, and writable storage on the real staging and production images. Owner: platform/infra. This is a hard gate; do not sequence engineering work before it clears.0.8.1. This converts an archived dependency into an owned artifact and removes "what if it disappears" from the critical path. Record the fork point and the diff-from-upstream policy (expected: none, forever).composer.json so a transitive bump cannot drift the driver out from under the app.async connection alongside the existing connection. Keep the current driver registered and configured — this is the rollback, and it should be tested as a rollback, not assumed.Compatibility. Treat upgrades of Laravel, PHP, or the base image as code-review events against the fork, not routine bumps — there is no upstream that will do this for you. On Octane and in test harnesses, watch for early connection resolution and provider boot order; that is the exact failure mode v0.8.1 patched, and the fork must be reviewed against it whenever the boot path changes. Also verify that queue:restart, queue:retry, and queue:failed behave sensibly in this model — operator muscle memory from queue:work will not transfer cleanly.
Sequencing rationale. The order above is deliberate: the platform gate is the cheapest possible kill point; the fork and pin make the dependency survivable before any code depends on it; adding alongside preserves rollback before risk is taken; and the canary precedes widening so the measured cost of spawn overhead informs scope rather than surprising it. Each phase has a named owner and a rollback, and no phase depends on the upstream project for anything.
Maintenance. The dominant cost is not the code, it is the ownership. Budget for: monitoring Laravel major releases for breaking changes the fork must absorb, re-validating the boot-order path on every Octane or framework upgrade, and periodically confirming the base image still permits process spawning. This is a small but permanent line item — the kind that gets quietly dropped after the launch milestone and discovered during an upgrade crunch. Name it in the runbook with a recurring review cadence.
Support. There is no vendor, no SLA, and zero dependents to lean on. First-line support is the internal fork owner; there is no upstream escalation. That should be stated plainly to stakeholders: this is not a supported product, and the internal team is the support contract. The upside worth communicating is that the surface area is small and the code path is well understood — a small team can genuinely carry it — provided the decision to do so is explicit and resourced.
Scaling. Per-job process spawn cost does not amortize; throughput is bounded by spawn overhead and concurrency settings, not by worker pool sizing. There is no built-in concurrency governor at scale, so burst behavior needs to be load-tested rather than assumed — a handful to low-tens of jobs per minute at peak is the rough envelope, and it should be measured against the actual workload before it is written into capacity plans. Scaling beyond that envelope is the primary exit trigger, and it should be defined numerically in advance so the migration is a scheduled program item rather than an incident response.
Failure modes to instrument and alert on. Two matter programmatically, and both are silent by default:
Alert on divergence between enqueued and completed counts per queue, and keep failed_jobs visibility wired up from day one. Additionally: confirm behavior when max_execution_time is hit (job dies, nobody notices), and treat proc_open being disabled by an infrastructure change as a latent failure mode that only appears on the next dispatch.
Ramp-up. Low for job authors — dispatch() is unchanged and existing job classes work as-is. That is a trap as much as a benefit: engineers will assume standard queue semantics unless told otherwise. Required enablement before the first production job:
max_execution_time and payloads stay small.Program framing. Position this as a bridge with a dated exit, not a foundation. Approve it for a defined window — "background work this quarter without platform investment" — and budget the driver swap into the next infrastructure cycle. The exit is a configuration change plus a re-test of idempotency assumptions, not a rewrite, which is precisely what makes the bridge defensible to stakeholders. Adoption without a named owner, a pinned fork, and a written exit trigger is the version of this decision that fails.
How can I help you explore Laravel packages today?