m-adamski/symfony-schedule-bundle
A Symfony bundle for scheduling recurring tasks and console commands. Define schedules in configuration, run jobs on cron-like intervals, and integrate cleanly with Symfony's console and dependency injection.
composer require m-adamski/symfony-schedule-bundle:^4.0
4.0.0 is a Symfony 7.x release. The changelog is a single line — Symfony 7.x — so treat the composer.json constraint as the real release note: confirm whether ^6.4 is still permitted before assuming this is a drop-in. If you're on Symfony 6.x, stay on the 3.x line and pin it; if you're on 6.4 and planning the 7 upgrade, do the bundle bump in the same window so you're not testing two major bumps independently.
If Flex doesn't auto-register it, add MAdamski\SymfonyScheduleBundle\ScheduleBundle::class => ['all' => true] to config/bundles.php. There is essentially no configuration to publish — with a bundle this size, the DI extension class (DependencyInjection/*Extension.php) is the documentation, and it's a five-minute read. Re-read it after the major bump; it's the file most likely to have moved or been renamed.
First use case: a single job. Wrap whatever you already do in a console command — e.g. pruning stale sessions — in a service that implements the bundle's task contract (check the Schedule/Task namespace for the exact interface and method names; it's the only real API surface), give it a cron expression, and add exactly one system cron entry:
* * * * * cd /var/www/app && bin/console schedule:run >> /dev/null 2>&1
Run it once by hand with bin/console schedule:run -vvv before trusting the cron. That single command is the entire mental model: a once-per-minute dispatcher that evaluates expressions and invokes due services.
On a fresh Symfony 7 app, read the last Gotcha before committing to this — the bundle now targets the same major as symfony/scheduler, so the "native is only for greenfield" argument no longer applies.
* * * * * cron plus a slow task means concurrent executions. Wrap the body in symfony/lock ($lock->acquire() / early return) rather than hoping jobs stay fast.dev/test via config (when@dev), not by commenting out cron entries, so the container stays consistent across environments.__invoke() directly and assert the cron expression as a string literal. For integration coverage, boot the kernel in a test and invoke schedule:run against a frozen clock. Symfony 7's Clock component makes the frozen-clock half much cleaner than it was on 5.4/6.x.config/bundles.php/service tags. Because job bodies live in your own classes the blast radius is small, but registration tags are the thing that silently breaks — a task that stops being tagged simply never runs, with no error.php.ini or the front controller; 30 2 * * *-style jobs skip or double-fire on DST transitions.max_execution_time. Anything heavy should be dispatched to a worker, not executed inline.debug:container --tag=<bundle task tag> shows whether the container actually sees your task. This is the single most likely 4.0.0 upgrade breakage. Then -vvv on schedule:run. Expression bugs (five fields vs. six, */5 vs. 5) are the next most common cause.composer update.symfony/scheduler does that natively, is first-party, and is stable. Use this bundle to move an existing 3.x/6.x codebase forward, or as a stepping stone — not as the starting point for new work. Migration is mostly deleting the interface and adding an attribute: a mechanical afternoon, not a rewrite.How can I help you explore Laravel packages today?