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

Cron File Generator Bundle Laravel Package

lexik/cron-file-generator-bundle

Lexik CronFileGeneratorBundle generates cron files from your Symfony console commands, letting you define scheduled tasks in configuration and export them to a crontab-ready file.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package — there is no service provider, no config/cron.php, and it will not install cleanly into a Laravel app. Treat this assessment as (a) how you'd use it if you touch a Symfony service, and (b) the pattern to steal for Laravel, where schedule:run is not an option (cron-only hosts, immutable containers, no long-running scheduler daemon).

As of 1.4.0 the bundle dropped its own env option (PR #16) — the rationale being that Symfony's Application already provides a global --env flag, so the bundle defining its own was redundant. Practical effect: bin/console lexik:cron:dump --env=prod still works (it's Symfony's option, not the bundle's), but re-diff the generated file after upgrading, because anything the bundle used to append from that option no longer appears on the emitted lines.

Minimal steps in a Symfony app:

composer require lexik/cron-file-generator-bundle
# Flex registers it; otherwise add to config/bundles.php
# config/packages/lexik_cron_file_generator.yaml
lexik_cron_file_generator:
    output_file: '%kernel.project_dir%/var/crontab'
    php_path: '/usr/bin/php8.3'
    console_path: '%kernel.project_dir%/bin/console'
    log_path: '%kernel.project_dir%/var/log'
    # 'environment' was dropped in 1.4.0 — delete it if you have it, or the
    # config tree may reject the key on upgrade.
    cron:
        - { name: 'cleanup', command: 'app:cleanup', expression: '0 3 * * *' }
        - { name: 'import',  command: 'app:import --force', expression: '*/15 * * * *' }
bin/console lexik:cron:dump --env=prod   # writes output_file

Where to look first, in this order:

  1. DependencyInjection/Configuration.php — the authoritative config tree. This is where the 1.4.0 change lands, and option names differ subtly between versions; check it before copying any YAML from a blog post.
  2. Command/DumpCronCommand.php — CLI flags and exit codes, so you can wire it into deploy scripts. After 1.4.0 there's one fewer flag here than older docs show.
  3. Service/CronFileGenerator.php — the exact line format it emits; this is the bit you'd reimplement in Laravel.

First use case: stop hand-editing crontabs on a box where every app:foo invocation must be documented, logged, and versioned in Git. The dumped file becomes an artifact you diff in CI.

Implementation Patterns

Deploy-time dump, not runtime. Never generate the crontab from a web request or the app's own scheduler. Add it as a post-deploy step:

bin/console lexik:cron:dump --env=prod
crontab var/crontab      # or: cp var/crontab /etc/cron.d/myapp (see gotchas)

Per-environment artifacts. Dump crontab.prod and crontab.staging with --env, keep both in the release tarball, install the right one per host. This is the main reason to use the bundle over a hardcoded file: one YAML source, N environments. Note that since 1.4.0 the env value is no longer a bundle-level config concern — it flows through Symfony's global --env, so the runtime environment the cron job actually boots into comes from the server environment (or a readable .env), not from the dump invocation. Verify on staging before you trust it in prod.

Docker/immutable images. Run the dump during image build (RUN bin/console lexik:cron:dump --env=prod), COPY the result in, and have the entrypoint install it. Because paths come from %kernel.project_dir%, build and runtime paths must match or you'll bake in a path that doesn't exist.

Per-job log files. Give noisy jobs their own log_file so failures are greppable; otherwise everything lands in the shared log_path. Generated lines typically look like:

*/15 * * * * /usr/bin/php8.3 /app/bin/console app:import --force >> /app/var/log/cron_import.log 2>&1

Upgrade recipe for 1.4.0. Treat it as a potential breaking change even though the version bump is minor:

# before
bin/console lexik:cron:dump --env=prod > /tmp/crontab.before
composer update lexik/cron-file-generator-bundle
# after
bin/console lexik:cron:dump --env=prod > /tmp/crontab.after
diff -u /tmp/crontab.before /tmp/crontab.after

If the diff touches every line, the env suffix changed and your installed crontab has been silently stale since the upgrade. Reinstall and re-baseline.

Call the service directly if you need the string in another command (e.g. a deploy:check command that asserts the installed crontab matches what would be dumped — a cheap drift detector).

Laravel port (the realistic path for you). Copy the format, not the code. A ~60-line cron:dump Artisan command reading config/cron.php:

foreach (config('cron.jobs') as $job) {
    $line = sprintf('%s %s %s %s >> %s 2>&1',
        $job['expression'], PHP_BINARY, base_path('artisan'), $job['command'], $job['log']);
    $lines[] = $line;
}
file_put_contents(base_path('var/crontab'), implode(PHP_EOL, $lines).PHP_EOL);

Then one real crontab entry (* * * * * php artisan schedule:run) or the full generated file, depending on your constraint. You get the same Git-diffable artifact without the Symfony dependency — and you skip the config-tree churn that just bit the upstream users.

Gotchas and Tips

  • It only writes a file. There is no runner, no lock, no retry, no failure alerting. It is a templating engine for text.
  • 1.4.0 removed the bundle's env option. If your YAML sets environment:, drop it and clear the container cache (bin/console cache:clear) — a leftover key can fail config validation or, worse, be silently ignored depending on your Symfony version. Also delete any internal doc/code that references a bundle-specific env flag; the surviving flag is Symfony's global one.
  • Re-baseline after any upgrade. Your drift detector compares an installed crontab to a fresh dump. A formatting change in a patch release shows up as "prod is drifted" on every host simultaneously — pin the version in composer.json (e.g. ^1.4.0) so upgrades are deliberate.
  • cron.d vs user crontab format. /etc/cron.d/* files require a user column between the schedule and the command; a user crontab does not. Install the dumped file with crontab <file> (user format) unless you verify the generator emits a user field. Mixing these up silently produces zero-run crontabs with no error.
  • Absolute paths or nothing. Cron's PATH is roughly /usr/bin:/bin. Use the full PHP binary path (which php on the target host, not your dev box) — version managers (phpbrew, asdf, Herd) will bite you in prod.
  • %kernel.project_dir% is resolved when the dump runs, so container path ≠ host path is a real failure mode. Assert the binary exists in your deploy pipeline.
  • The output file is overwritten, not merged. Never point output_file at a crontab that humans also edit — someone's MAILTO header or manual entry will vanish on the next deploy.
  • Log directory must pre-exist and be writable by the cron user. The bundle won't mkdir for you, and cron swallows the error.
  • No overlap protection. A job taking >15min on a */15 schedule will stack. Wrap the command yourself: flock -n /tmp/app-import.lock php ....
  • Shell quoting is minimal. Commands with quotes, pipes, or > are passed through largely verbatim; you're responsible for escaping. Quote paths that could contain spaces.
  • Environment/.env nuance. --env=prod selects the environment for the dump, not for the job. The runtime env still comes from the server environment (or a .env the cron user can read). Don't assume your dev .env applies.
  • Symfony-only, low maintenance signal. 23 stars, zero dependents, and a minor-version release that changes option handling with a one-line changelog. Pin the version. On the Laravel side, vendor the ~50 lines of formatting logic rather than fighting symfony/config into a Laravel container — you'd have inherited this upgrade's diff for no benefit.
  • Drift detection is your friend. A cron:diff command that compares installed crontab to a fresh dump catches "someone hotfixed prod" before the next deploy silently reverts it — and is the fastest way to see what 1.4.0 actually changed on your hosts.
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