Skip to content
Kona

Task scheduling

Register recurring work on a Schedule, then run due tasks with grind. Pair this with a system cron entry that calls ./grind schedule:run every minute, or run ./grind schedule:work as a long-lived process.

Register ScheduleServiceProvider in your application bootstrap.

val schedule = app.resolve(Schedule::class.java)
schedule.everyMinute("heartbeat") { ping() }
schedule.dailyAt("reports", hour = 8, minute = 0) { buildReports() }
schedule.command("custom", "*/15 * * * *") { sync() }
Schedule schedule = app.resolve(Schedule.class);
schedule.everyMinute("heartbeat", () -> ping());
schedule.dailyAt("reports", 8, 0, () -> buildReports());
Terminal window
./grind schedule:list
./grind schedule:run
./grind schedule:work --max=1 --sleep=60
Method Cron
everyMinute * * * * *
everyFiveMinutes */5 * * * *
everyTenMinutes */10 * * * *
everyFifteenMinutes */15 * * * *
everyThirtyMinutes */30 * * * *
hourly 0 * * * *
everyTwoHours … everySixHours 0 */n * * *
daily 0 0 * * *
dailyAt(name, hour, minute) minute hour * * *
weekly 0 0 * * 0
monthly 0 0 1 * *
command(name, expression, task) Custom 5-field cron

Cron fields are min hour dom month dow. Supported atoms: *, numbers, lists (0,30), ranges (1-5), and steps (*/n).

Each registration returns a ScheduledEvent you can configure:

schedule.everyFiveMinutes("invoice-sync") { syncInvoices() }
.withoutOverlapping(expiresMinutes = 10)
schedule.hourly("cleanup") { purgeTemp() }
.onOneServer() // file lock under storage/framework
.runInBackground() // virtual thread; schedule:run returns immediately
  • withoutOverlapping — skip if the previous run still holds the mutex
  • onOneServer — file-based lock so only one machine runs the task
  • runInBackground — start the task on a virtual thread
Command Behavior
schedule:list Show registered event names (and flags)
schedule:run Run events due at the current minute
schedule:work Loop: run due events, sleep (--sleep=60), optional --max ticks

Typical production setup: system cron * * * * * cd /app && ./grind schedule:run. Use schedule:work when you prefer a single long-lived JVM instead of minute cron.