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

Daemonizable Command Laravel Package

wrep/daemonizable-command

Laravel package for turning Artisan commands into long-running daemons. It provides a base command with loop control, graceful shutdown, signal handling, and start/stop/restart support, making it easier to build reliable background workers and scheduled tasks.

Deep Wiki
Context7

Technical Evaluation

Architecture fit — the central finding. This package is a Symfony Console library, not a Laravel package. It ships no service provider, no config file, and no Laravel-facing API. Its value is a DaemonizableCommand base class that wraps execute() in an endless while loop and uses pcntl_signal to break out on SIGTERM/SIGINT.

Laravel's Artisan commands extend Illuminate\Console\Command, which is itself a Symfony Command subclass. DaemonizableCommand is a sibling, not an ancestor. You cannot extend both, so adoption forces a choice: keep Laravel's command ergonomics ($this->info(), option(), argument(), confirm(), table(), progressBar(), handle() convention, $this->laravel, call()) or get the loop. That is a real DX tax for a fairly small amount of code.

Integration feasibility. Composer install is trivial and version constraints (PHP 8.x, Symfony Console 6/7) are fine. The gate is the pcntl extension:

  • Not available on Windows, so local dev and any Windows CI runner is out.
  • Frequently not compiled into stock PHP CLI images; needs docker-php-ext-install pcntl and a rebuild of the build pipeline.
  • Historically blocked on some shared hosts.

Mechanism risk. The package relies on declare(ticks=1) for signal dispatch, which is a known footgun — the declaration must be in the entrypoint script that runs the loop, not just the class. Ticks-based dispatch also carries a measurable per-statement overhead and is superseded in practice by pcntl_async_signals(true). Separately, Laravel 10.x+ already exposes Command::trap() / untrap() via a signals trait — verify against your target version, but if it holds, the package's headline feature is available in-framework in roughly ten lines.

Technical risk.

  • Signal handling is best-effort by definition: SIGKILL (OOM killer, docker stop past the grace window, Supervisor stopwaitsecs expiry) is uncatchable. Graceful shutdown is never guaranteed.
  • No recycling. queue:work gives you --max-time, --max-jobs, --memory; this gives you nothing. Long-running PHP means RSS drift, MySQL wait_timeout disconnects, Redis idle drops, and stale Eloquent/container state.
  • It solves the loop, not the operations: no supervision, restart backoff, crash metrics, or deploy-restart hook.
  • Maintenance concentration: 226 stars, overall score 2.69, and an opportunity score of 0.38 — the market signal says the unmet need here is narrow, because queue:work, schedule:work, Horizon, and Octane already occupy the daemon niche.

Key questions to resolve before committing.

  1. What is the actual workload — scheduled jobs, queue consumers, or a genuinely long-lived poller/socket listener? If it is a queue consumer, stop: queue:work is strictly better.
  2. Which deployment targets lack pcntl, and who pays for the image rebuild?
  3. Who owns Supervisor/systemd unit authoring, grace periods, and restart policy?
  4. Is the job idempotent, and will it ever run as more than one process? The package offers no locking or partitioning.
  5. Do we accept a single-maintainer dependency for a 30-line loop?

Integration Approach

Stack fit. Language and framework versions are not the problem — PHP 8.x and Symfony Console 6/7 are fine under Laravel 10–12. The friction is entirely the base-class fork and the pcntl requirement.

Migration path — evaluate in this order, escalate only on evidence.

  1. Stay short-lived. Keep commands one-shot and let schedule:run + cron, or schedule:work under Supervisor, drive frequency. Zero new dependencies; covers the large majority of "we think we need a daemon" requests.
  2. Laravel-native worker. queue:work --max-time=3600 --max-jobs=1000 --memory=256 under Supervisor. You get free recycling, graceful shutdown, retry semantics, failed_jobs visibility, and Horizon if the team already runs it.
  3. In-house loop. Illuminate\Console\Command + while + pcntl_async_signals(true) + $this->trap([SIGTERM, SIGINT], ...). Roughly 30–50 lines, keeps every Laravel helper, no third-party base class.
  4. Adopt this package. Only if tiers 1–3 are demonstrably inadequate. Cost: re-implement or inject output helpers manually, add declare(ticks=1) to artisan, add pcntl to all images, and own the Symfony/Laravel version coupling on every major upgrade.

Sequencing (4–6 weeks to a decision gate).

  • Week 1 — Spike. One command, one container with pcntl, 24-hour soak. Instrument RSS, iteration latency, and signal handling. Do not scope this as a production build.
  • Week 2 — Inventory. Classify every existing cron job and long-poll: queueable, schedulable, or genuinely long-lived. Expect the third bucket to be near-empty.
  • Week 3 — Pilot. Lowest-risk genuinely-long-lived job behind Supervisor, with stopwaitsecs strictly inside the container grace period.
  • Week 4–5 — A/B. Run the same job on the queue:work path. Decide on operational evidence — restart behavior, memory profile, incident count — not on API elegance.
  • Week 6 — Gate. Standardize or retire. Publish the outcome so the next team doesn't re-litigate it.

Operational Impact

Maintenance. The public surface is small — loop control, interval, timeout, break-on-signal — which means low churn but also a thin abstraction you will end up reading the source of. Pin the version; treat upgrades as a review event given single-maintainer concentration. The larger recurring cost is the base-class fork: every Laravel major upgrade requires re-validating how much of Illuminate\Console\Command you have given up.

Support. No provider, no config, no docs site. Knowledge lives in the README and the source. Your platform team — not the vendor — owns signal semantics, Supervisor tuning, and deploy-restart behavior. That is a permanent ownership transfer for a small feature.

Scaling. Vertical only, per process. You scale by running N supervised processes, which immediately reintroduces coordination problems the package does not address: work partitioning, duplicate claims, idempotency, backpressure. No locking primitives. Any workload that needs horizontal scale belongs on a queue; this is the wrong layer for it.

Failure modes to design for.

Failure Symptom Mitigation
SIGKILL / OOM / grace expiry Partial work, skipped cleanup hook Idempotent jobs; bounded units of work
Memory growth OOM restarts read as random crashes External recycle timer or --max-time equivalent
MySQL wait_timeout / Redis idle drop Uncaught exception kills the loop Reconnect-on-exception wrapper; Supervisor backoff
No max runtime Old code runs indefinitely after deploy Explicit restart-on-deploy in the release pipeline
Two instances, non-idempotent job Duplicate side effects Distributed lock; single-instance guarantee via Supervisor
Thin observability Log flooding, no iteration health Emit heartbeat + duration metrics; alert on loop silence

Ramp-up. Half a day to write the first command. Two to three weeks of operational tuning — grace periods, restart backoff, memory ceilings, deploy hooks, alerting on silent loops — before it is production-trustworthy. Cheaper to start than a queue, more expensive to operate than cron.

Recommendation. Default to Laravel-native primitives. Approve this package only for a genuinely long-lived, single-instance, stateful process that cron and queue:work cannot express — and only with a named owner for the pcntl/Supervisor surface. The 0.38 opportunity score is the correct read: the gap this fills has largely been closed by the framework itself.

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