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

Sansdaemon Laravel Package

queueworker/sansdaemon

Run Laravel queue workers without a long-running daemon. Sansdaemon processes queued jobs on demand, making it ideal for cron, serverless, and shared hosting where persistent workers are not possible. Install via Composer, configure your queues, and let it drain jobs and exit cleanly.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm what it registers. The package's whole premise is that you never run a long-lived queue:work process; instead you run a short-lived process from cron, it drains a batch of jobs, and then exits (killing itself). That shapes everything below.

composer require queueworker/sansdaemon
php artisan vendor:publish --provider="Queueworker\Sansdaemon\SansdaemonServiceProvider"
php artisan list | grep -i sansdaemon   # see the exact command + flags for your installed version

Check the package's composer.json constraints against your Laravel version before you build on it — the last release was March 2023, so it is aimed at the Laravel 8/9 era worker API. If composer require fights you on Laravel 10/11, treat that as a signal rather than something to force with --ignore-platform-reqs.

Where to look first. In this order: the README's cron example, the package's console command class (to see which Illuminate\Queue\Worker options it passes through — --tries, --timeout, --queue, --stop-when-empty), then the config file if one was published. That tells you whether it is a thin wrapper over the worker or a custom worker subclass.

First use case. Shared hosting or single-container deploys where you have cron but no Supervisor/systemd and no Horizon. Wire it into the scheduler once and watch it work:

// routes/console.php (Laravel 11+) or app/Console/Kernel.php (10 and below)
$schedule->command('sansdaemon:work --stop-when-empty')
    ->everyMinute()
    ->withoutOverlapping();

Then prove the loop end-to-end: dispatch a trivial job, run php artisan schedule:run twice by hand, confirm the job is picked up, the process exits, and nothing is left in jobs. Only after that should you trust it to cron.

Implementation Patterns

Cron → scheduler → batch worker. Don't point cron at the package command directly; point cron at schedule:run and let the scheduler own the cadence. It keeps withoutOverlapping() (which uses the cache lock) and onOneServer() available, both of which you will want.

* * * * * cd /var/www/app && php artisan schedule:run >> /dev/null 2>&1

Separate schedule entries per queue. Because each invocation is a fresh process, you get cheap isolation between queues — one entry for --queue=high, one for default, one for low, with different frequencies. This is the main advantage over a single daemon and it costs you nothing since you are not paying for long-lived memory.

$schedule->command('sansdaemon:work --queue=high')->everyMinute()->withoutOverlapping();
$schedule->command('sansdaemon:work --queue=default,low')->everyFiveMinutes()->withoutOverlapping();

Tune the batch size, not the sleep. The whole point is that the process dies, so there is no --sleep to lean on between jobs. Instead control how much work one invocation claims (--max-jobs, --max-time, or --stop-when-empty depending on what your version exposes) so a run comfortably finishes inside its cron window rather than spilling into the next minute.

Local development. Keep using queue:work on your machine — it is faster to iterate on and gives you live output. Use SansDaemon as the production execution model and reserve php artisan <command> -v for reproducing production-only issues.

Deployment. Your deploy script gets simpler: no supervisorctl restart, no .conf files in the repo. But replace that lost supervision with something: a failed-run alert (Healthchecks.io ping on success, Sentry for exceptions), and queue:failed reviewed on a schedule. Removing Supervisor removes the thing that used to restart you silently.

Job-side hygiene matters more here. Because each process boots the framework fresh, every job effectively pays full bootstrap cost and you lose in-process warm state. Keep jobs small, idempotent, and self-contained; push shared setup into the job's handle() rather than relying on anything that used to persist between jobs.

Gotchas and Tips

withoutOverlapping() is not optional. A job that runs longer than your cron interval will otherwise spawn a second worker competing for the same job. The lock saves you, but you also need a $timeout on the job and --timeout on the command that are both meaningfully lower than the interval. Three timeouts (cron gap, worker --timeout, job $timeout) that disagree is the number one source of duplicate execution here.

"Kills the process" means exit codes are your only telemetry. There is no supervisor capturing stderr and restarting. Log the command output, and check the exit code in the scheduler output or a wrapper script; otherwise a crashing worker looks identical to an idle one.

Overlap with Laravel's built-in --stop-when-empty. Modern Laravel can already do queue:work --stop-when-empty from cron. If your version supports it cleanly, the package's remaining value is mostly its worker subclass and defaults. Benchmark it against vanilla before adopting a dependency with 178 stars, zero dependents, and no release in over a year — that combination means you are the maintainer now.

Missing failed_jobs table. Without Supervisor you get no loud failure; jobs that throw will vanish unless the table exists and --tries is set. Run php artisan queue:failed-table && php artisan migrate and actually check it, or wire Queue::failing() to your alerting.

Shared-host process limits. If proc_open/exec are disabled or you have a hard 60-second CPU cap, the "kill the process" behavior is working with you — but a single slow job will be SIGKILLed mid-flight and retried from scratch. Make jobs resumable rather than restartable-from-zero.

Debugging a single job. php artisan queue:work --once -v bypasses the package entirely and is usually the fastest way to isolate whether a failure is job logic or dispatch/worker wiring. Then php artisan queue:failed, queue:retry all, and queue:flush for cleanup.

Extension points. Listen on Illuminate\Queue\Events\JobProcessing, JobProcessed, JobFailed, and WorkerStopping to add metrics or structured logging — they fire the same way regardless of which worker implementation is running, so your observability survives if you later drop the package for vanilla cron + --stop-when-empty. If you need custom behavior on shutdown, WorkerStopping is the hook that fires just before the process dies.

Version-pin it. Given the release history, pin an exact version and add a smoke test that asserts a job actually processes through the cron path. That test is what will catch a Laravel upgrade breaking the worker signature before production does.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console