psf1/cron-bundle
psf1/cron-bundle is a Symfony bundle for defining, scheduling, and running cron jobs from your application. It helps manage recurring tasks in one place, reducing reliance on system crontabs and making scheduled work easier to configure and maintain.
Read this first: psf1/cron-bundle is a Symfony bundle, not a Laravel package. It ships no ServiceProvider, no facade, and no Eloquent support — it assumes Symfony's DependencyInjection container, the Console component, and (typically) Doctrine ORM for persistence. It remains effectively unmaintained by community standards (zero stars, no dependents), and 1.0.2 is a patch-level release that adds a feature and a README refresh — not a revival. The realistic question isn't "how do I install this into Laravel" but "do I want to port its model, or just reproduce it with Laravel-native tools?" The 1.0.2 answer is still: reproduce it. Laravel's container already gives you the one thing this release added.
If you still want to use it directly:
composer require psf1/cron-bundle — expect Symfony components (and likely Doctrine) to come along.config/bundles.php and run whatever install/migration command it ships. In Laravel, you'd have to instantiate its core services by hand inside a service provider you write yourself — and since 1.0.2, also hand-register whatever constructor dependencies your job classes declare, because you're the container now.name, command (or request), schedule (cron expression), enabled, and some notion of lastRun / nextRun.* * * * *) that calls the bundle's "tick" command; the bundle then evaluates every persisted job and dispatches the ones that are due.Where to look first:
Entity/ (or Model/) directory — that's the schema you'll be adapting or porting.new a job and now asks the container for it. That is the file that tells you how far the DI support actually reaches.First use case: per-tenant scheduling. A user creates a recurring report or webhook; you persist a row with an expression like */15 * * * * and the command or URL to invoke. The tick command does the rest.
crontab -e mutation pattern) unless you truly must. One minute-level tick command that reads due jobs from the database is more portable, testable, and avoids shelling out on every user action.dragonmantank/cron-expression (CronExpression::factory($expr)->isDue()) reproduces ~90% of what this bundle does and avoids mixing Doctrine and Eloquent in one app. This is the pattern most Laravel devs will end up with.app()->make($jobClass) resolves constructor dependencies automatically, including queued jobs. So persist a job class name (or a job payload) rather than a raw shell command, and your runner dispatches it. You get DI, retries, timeouts, and logs for free instead of reimplementing them around exec().Cache::lock("cron:{$job->id}") (or a DB advisory lock) so a slow run can't double-fire when the next cron minute overlaps.last_started_at, last_finished_at, last_status, and last_output. These are the columns the bundle is most likely not to give you and the ones you'll miss most in production.app()->make(...)), and the job classes must be resolvable — any new MyJob() in ported code now breaks the moment a job declares a constructor arg. Also verify the autowiring actually applies to your job classes; a README-level claim is not the same as coverage across the tick path.crontab rights and breaks inside containers and most managed hosts. The DB + tick approach sidesteps this entirely — prefer it.timezone on the row and evaluate with it.CronExpression::isValidExpression() before saving. Otherwise one bad row can break the whole tick loop depending on how the runner handles parse errors.* * * * * tick misses minutes during deploys or slow previous runs. Make the runner idempotent: select jobs where next_run <= now, advance next_run transactionally, don't store booleans like is_due.name/command/schedule/enabled, map schedule onto a cron column on a custom model, convert each stored command into a job class if you haven't already (1.0.2 pushes you in that direction anyway), and replace the tick with a single schedule:run-style loop. No data loss, no behavioral change — which is exactly why most Laravel teams skip the bundle and start here.How can I help you explore Laravel packages today?