ralphjsmit/laravel-horizon-cron-supervisor
Run Laravel's scheduled tasks through Horizon instead of system cron. Your schedule is dispatched as Horizon jobs, so it's supervised, monitored, and retried automatically and visible on the Horizon dashboard.
Architecture fit. Laravel's deployment guidance assumes an external process manager — Supervisor or systemd — keeps Horizon alive. Where that assumption doesn't hold (cPanel/Plesk, a single small VPS, cron-only PaaS, containers with no init process, staging or on-prem environments where the customer owns the box and won't install a process manager), Horizon has no supported liveness story at all. This package fills exactly that gap using primitives the host already provides: one artisan command and one cron entry. It adds no new daemon, no schema change, no queue-driver change, and it reuses the existing config/horizon.php supervisors and queues. That is close to the minimum possible footprint for the capability, and it is the strongest argument for adopting it.
The one thing a TPM must resolve before scoping: the source materials describe two different mechanisms, and they are not equivalent. The package description implies a liveness poll — cron checks whether Horizon is running and starts it if not. The prior assessment describes a bounded run — cron invokes a command that boots Horizon, drains jobs for a max runtime or job count, exits gracefully, and is re-invoked next tick. These imply different failure modes (stale-PID false positives vs. overlapping masters and throughput gaps), different tuning parameters (none vs. max runtime under cron interval), and different deploy interactions. Confirming which one the code implements is the first task in the spike, not an implementation detail. Any estimate built before that is confirmed is provisional.
Integration feasibility. High, inside a deliberately narrow envelope. Adoption is composer require, optional config publish, and a cron line under the deploy user. Rollback is deleting the cron line and re-enabling the prior supervisor. No application-code change, no data migration, no dashboard reconfiguration, and no new dependency in the runtime stack — Laravel + Horizon + Redis are already present if Horizon is a candidate at all. The real integration cost isn't engineering hours; it's decision and documentation work: confirming you're genuinely in the target environment, and rewriting the on-call runbook to describe the new restart semantics.
Technical risk. Risks are operational rather than code-quality, and most are verifiable inside one day of staging work:
horizon:terminate and rely on the supervisor restarting immediately on new code. Under cron, restart waits for the next tick — up to a minute of queue stall per deploy. Fixable in the pipeline, but it must be sequenced, not discovered in production.Key questions.
horizon:terminate during deploys, and what pipeline change avoids the stall?Stack fit. Best fit: single-server deployments, cPanel/Plesk/semi-managed hosting, small teams without config management, containers lacking a process supervisor, staging and QA environments that want production-like Horizon behavior without a second class of infrastructure, and single-tenant on-prem installs where the customer controls the server. Decline outright for Kubernetes (liveness probe plus a proper controller), ECS with a service definition, Vapor, Forge with native daemons, or any host already running Supervisor or systemd — there the package is strictly redundant complexity and introducing it degrades a working setup.
Migration path. Low ceremony and reversible, but don't skip the measurement steps:
composer.json; install and publish config on staging.Compatibility. Verify the package's Laravel/PHP/Horizon constraints against the app's pinned versions before anything else. Laravel 12 support landed in 1.4.3 (March 2025); older Laravel lines should be confirmed against the composer constraints rather than assumed. Horizon major version is the sharper edge — the package wraps Horizon's supervisor lifecycle, so a Horizon internals change is more likely to break it than a Laravel bump. Pin Horizon and this package together, and treat a Horizon upgrade as requiring a re-read of the source and a staging kill-test.
Sequencing. Treat this as two decoupled releases rather than one:
Rollback at any point in Release A is deleting the cron line and re-enabling the prior mechanism — which is exactly why a tested fallback should be prepared before cutover rather than after.
Maintenance. Small and bounded: a pinned Composer dependency, one config file, one cron entry, and a paragraph in the runbook. The continuing obligation is not patching — it's the version-watch duty on Horizon upgrades, since this package tracks Horizon's lifecycle internals. Assign a named owner in the dependency inventory and record the tested fallback (a hand-written scheduled liveness command, or a Supervisor unit that can be re-enabled) alongside it. Absent an owner, a low-adoption single-maintainer package on the job-processing path becomes an unowned availability risk.
Support. Community-only: MIT, GitHub issues, no vendor backing, no SLA. Acceptable for glue you could replace in an afternoon; not acceptable where compliance or audit requires vendor-backed support — that bar should be checked explicitly rather than assumed irrelevant. Budget roughly a day of engineering for the source read and staging kill-test, which is also the cost of being able to say you vetted it.
Scaling. This is a single-node, modest-volume solution by construction. It does not autoscale, and under the bounded-run model it does not continuously hold worker capacity — capacity exists in windows. Two thresholds mark the ceiling: (a) queue depth does not drain within a cycle at expected peak, and (b) job age becomes a product-visible number. Multi-node Horizon fleets need real process supervision and health checks; the package should not be stretched to cover them. Write the ceiling down now, so the eventual migration is a planned step rather than an incident response.
Failure modes. Enumerate them and monitor each independently — do not rely on the supervisor to report its own failure:
horizon:terminate with no immediate restart; queue halts until the next tick.Ramp-up. Very low for an engineer already comfortable with Horizon. The genuinely new knowledge is narrow and testable: how this command determines liveness and shutdown, and what the host's actual cron granularity is. On-call needs one rewritten runbook page — "queue is stalled, check whether Horizon is running and re-invoke the command" — plus the two new alerts (process age, queue depth) and a documented manual restart procedure that does not depend on cron firing. The trap is assuming cron behaves like Supervisor: it polls, it doesn't react, and the difference shows up as a minute of queue stall at the worst possible moment — during a deploy.
How can I help you explore Laravel packages today?