Skip to main content
Kit provides a powerful task scheduling system inspired by Laravel’s scheduler. Schedule tasks to run at specific intervals - every minute, hourly, daily, weekly, or using custom cron expressions.

Generating Tasks

The fastest way to create a new scheduled task is using the Kit CLI:
kit make:task CleanupLogs
This command will:
  1. Create src/tasks/cleanup_logs_task.rs with a task stub
  2. Create src/tasks/mod.rs if it doesn’t exist
  3. Create src/schedule.rs for registering tasks
  4. Create src/bin/schedule.rs scheduler binary
# Creates CleanupLogsTask in src/tasks/cleanup_logs_task.rs
kit make:task CleanupLogs

# Creates SendRemindersTask in src/tasks/send_reminders_task.rs
kit make:task SendReminders

# You can also include "Task" suffix (same result)
kit make:task BackupDatabaseTask

Defining Schedules

Kit supports two approaches for defining scheduled tasks: For complex tasks that need dependencies or reusable logic, implement the Task trait and configure the schedule during registration:
// src/tasks/cleanup_logs_task.rs
use async_trait::async_trait;
use kit::{Task, TaskResult, DB};
use crate::models::Log;

pub struct CleanupLogsTask;

impl CleanupLogsTask {
    pub fn new() -> Self {
        Self
    }
}

#[async_trait]
impl Task for CleanupLogsTask {
    async fn handle(&self) -> TaskResult {
        // Access the database just like in controllers
        let db = DB::connection();

        // Delete logs older than 30 days
        Log::query()
            .filter(Log::created_at.lt(thirty_days_ago()))
            .delete()
            .await?;

        println!("Old logs cleaned up successfully");
        Ok(())
    }
}
Then register with fluent scheduling API in src/schedule.rs:
// src/schedule.rs
use kit::Schedule;
use crate::tasks::CleanupLogsTask;

pub fn register(schedule: &mut Schedule) {
    schedule.add(
        schedule.task(CleanupLogsTask::new())
            .daily()
            .at("03:00")
            .name("cleanup:logs")
            .description("Removes logs older than 30 days")
    );
}

2. Closure-Based Tasks

For quick, inline tasks without separate files:
// src/schedule.rs
use kit::Schedule;

pub fn register(schedule: &mut Schedule) {
    // Simple closure task
    schedule.add(
        schedule.call(|| async {
            println!("Ping! Running every minute");
            Ok(())
        })
        .every_minute()
        .name("heartbeat")
    );

    // Configured closure task
    schedule.add(
        schedule.call(|| async {
            // Your task logic
            Ok(())
        })
        .daily()
        .at("09:00")
        .name("morning-report")
        .description("Sends daily morning report")
    );
}

Registering Tasks

Register your tasks in src/schedule.rs:
// src/schedule.rs
use kit::Schedule;
use crate::tasks;

pub fn register(schedule: &mut Schedule) {
    // Trait-based tasks with fluent schedule configuration
    schedule.add(
        schedule.task(tasks::CleanupLogsTask::new())
            .daily()
            .at("03:00")
            .name("cleanup:logs")
            .description("Removes logs older than 30 days")
    );

    schedule.add(
        schedule.task(tasks::SendRemindersTask::new())
            .daily()
            .at("09:00")
            .name("send:reminders")
            .description("Sends daily reminder emails")
    );

    schedule.add(
        schedule.task(tasks::BackupDatabaseTask::new())
            .weekly()
            .at("00:00")
            .name("backup:database")
            .description("Weekly database backup")
            .without_overlapping()
    );

    // Closure-based tasks
    schedule.add(
        schedule.call(|| async {
            println!("Quick task!");
            Ok(())
        })
        .hourly()
        .name("quick-task")
    );
}

Schedule Frequency Options

Kit provides a fluent API for defining when tasks should run:

Common Intervals

MethodDescription
.every_minute()Run every minute
.every_five_minutes()Run every 5 minutes
.every_ten_minutes()Run every 10 minutes
.every_fifteen_minutes()Run every 15 minutes
.every_thirty_minutes()Run every 30 minutes
.hourly()Run every hour at minute 0
.hourly_at(30)Run every hour at minute 30
.daily()Run daily at midnight
.daily_at("03:00")Run daily at 3:00 AM
.weekly()Run weekly on Sunday at midnight
.monthly()Run monthly on the 1st at midnight

Day-Specific Schedules

use kit::DayOfWeek;

// Run on specific days
.weekly_on(DayOfWeek::Monday)
.weekly_on(DayOfWeek::Friday)

// Shorthand day methods
.sundays()
.mondays()
.tuesdays()
.wednesdays()
.thursdays()
.fridays()
.saturdays()

// Multiple days
.days(&[DayOfWeek::Monday, DayOfWeek::Wednesday, DayOfWeek::Friday])

// Weekdays/Weekends
.weekdays()  // Monday-Friday
.weekends()  // Saturday-Sunday

Time Modifiers

Chain .at() with any schedule to set a specific time:
.daily().at("14:30")           // Daily at 2:30 PM
.weekly().at("09:00")          // Weekly at 9:00 AM
.mondays().at("08:00")         // Every Monday at 8:00 AM
.monthly().at("00:00")         // First of month at midnight

Custom Cron Expressions

For full control, use cron syntax:
// Standard cron format: minute hour day-of-month month day-of-week
.cron("0 */2 * * *")    // Every 2 hours
.cron("30 4 * * 1-5")   // 4:30 AM on weekdays
.cron("0 0 1,15 * *")   // 1st and 15th of each month

Task Configuration

Preventing Overlapping

Prevent a task from running if a previous instance is still executing:
schedule.add(
    schedule.task(LongRunningTask::new())
        .daily()
        .name("long-task")
        .without_overlapping()
);

Running in Background

Run tasks without waiting for completion:
schedule.add(
    schedule.task(BackgroundTask::new())
        .hourly()
        .name("background-task")
        .run_in_background()
);

Running the Scheduler

Kit provides CLI commands for running scheduled tasks:

Run Once

Execute all due tasks once (typically called by cron every minute):
kit schedule:run

Daemon Mode

Run continuously, checking for due tasks every minute:
kit schedule:work
This is ideal for development or when using a process manager like systemd.

List Tasks

Display all registered scheduled tasks:
kit schedule:list
Output:
Scheduled Tasks:
==========================================================================================
Name                           Schedule                       Description
------------------------------------------------------------------------------------------
cleanup:logs                   0 3 * * *                      Removes logs older than 30 days
send:reminders                 0 9 * * *                      Sends daily reminder emails
backup:database                0 0 * * 0                      Weekly database backup
==========================================================================================
Total: 3 task(s)

Run Specific Task

Run a specific task by name:
cargo run --bin schedule -- run-task cleanup:logs

Production Setup

Using Cron

Add a single cron entry to run the scheduler every minute:
* * * * * cd /path/to/your/project && kit schedule:run >> /dev/null 2>&1

Using Systemd

Create a systemd service for the scheduler daemon:
# /etc/systemd/system/myapp-scheduler.service
[Unit]
Description=MyApp Scheduler
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/path/to/your/project
ExecStart=/path/to/kit schedule:work
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl enable myapp-scheduler
sudo systemctl start myapp-scheduler

Accessing App Context

Scheduled tasks have full access to the application context, just like controllers:
use kit::{App, Task, TaskResult, DB};
use crate::actions::SendEmailAction;
use crate::models::User;

pub struct SendRemindersTask;

#[async_trait]
impl Task for SendRemindersTask {
    async fn handle(&self) -> TaskResult {
        // Access database
        let users = User::query()
            .filter(User::reminder_enabled.eq(true))
            .all()
            .await?;

        // Use actions via dependency injection
        let send_email: SendEmailAction = App::get().unwrap();

        for user in users {
            send_email.execute(&user.email, "Daily Reminder").await?;
        }

        Ok(())
    }
}

File Organization

The recommended file structure for scheduled tasks:
src/
├── tasks/
│   ├── mod.rs              # Re-export all tasks
│   ├── cleanup_logs_task.rs
│   ├── send_reminders_task.rs
│   └── backup_database_task.rs
├── schedule.rs             # Register tasks
├── bin/
│   └── schedule.rs         # Scheduler binary
├── bootstrap.rs
├── routes.rs
└── main.rs
src/tasks/mod.rs:
pub mod cleanup_logs_task;
pub mod send_reminders_task;
pub mod backup_database_task;

pub use cleanup_logs_task::CleanupLogsTask;
pub use send_reminders_task::SendRemindersTask;
pub use backup_database_task::BackupDatabaseTask;

Summary

FeatureUsage
Create taskkit make:task TaskName
Trait-basedImplement Task trait, configure schedule during registration
Closure-basedschedule.call(|| async { ... })
Register tasksschedule.add(schedule.task(...).daily().name("..."))
Run oncekit schedule:run
Run daemonkit schedule:work
List taskskit schedule:list
Prevent overlap.without_overlapping()
Background.run_in_background()