Kestrun Scheduling (Jobs & CRON)
Kestrun’s built-in Scheduler lets your server run background jobs on fixed intervals or CRON expressions, in either C# or PowerShell.
Capabilities
- Register jobs — interval or 6‑field CRON (seconds precision)
- Run work in-process:
- C#: any
Func<CancellationToken, Task>delegate, or inline code compiled via Roslyn - PowerShell: inline
ScriptBlockor a.ps1file
- C#: any
- Pause / resume / cancel jobs at runtime
- Query live status (UTC or any time-zone) as a strongly-typed report or PowerShell hashtable
- Operate entirely from PowerShell cmdlets or directly via C# APIs
Enabling the scheduler (C#)
var host = new KestrunHost("MyApp");
host.EnableScheduling(); // queues the feature
host.ApplyQueuedFeatures(); // executed once before StartAsync
Enabling creates a shared PowerShell Runspace pool for PS jobs and attaches a SchedulerService instance at host.Scheduler.
Registering jobs
PowerShell (inline)
Register-KrSchedule -Name Cache -Interval '00:15:00' -ScriptBlock {
Write-KrInfo "⏰ Cache refresh at $(Get-Date -f o)"
}
PowerShell (script file via CRON)
Register-KrSchedule -Name Nightly -Cron '0 0 3 * * *' -ScriptPath 'Scripts/Cleanup.ps1'
C# (inline)
host.Scheduler.Schedule(
name: "heartbeat",
interval: TimeSpan.FromSeconds(10),
job: ct => { Log.Information("💓 {Now:O}", DateTimeOffset.UtcNow); return Task.CompletedTask; },
runImmediately: true);
C# (CRON)
host.Scheduler.Schedule("cleanup", "0 0 3 * * *", async ct => await CleanupAsync(ct));
Controlling jobs
Suspend-KrSchedule -Name Nightly # pause
Resume-KrSchedule -Name Nightly # resume
From C#, use Pause(name), Resume(name), or Cancel(name) on SchedulerService.
Reporting
# Strongly-typed .NET report in UTC
$report = Get-KrScheduleReport
# Same, but Pacific time and as Hashtable
Get-KrScheduleReport -TimeZoneId 'Pacific Standard Time' -AsHashtable
# Quick snapshot (wildcards allowed)
Get-KrScheduleSnapshot -Name 'ps-*' -AsHashtable | ConvertTo-Json -Depth 3
Sample (trimmed):
{
"generatedAt": "2025-07-24T22:10:30Z",
"jobs": [
{ "name":"heartbeat","lastRunAt":"2025-07-24T22:10:28Z","nextRunAt":"2025-07-24T22:10:38Z","isSuspended":false }
]
}
API reference (C#)
| Method | Purpose |
|---|---|
Schedule(string name, TimeSpan interval, Func<…> job, bool runImmediately=false) | Register interval job (C# delegate) |
Schedule(string name, string cron, Func<…> job, bool runImmediately=false) | Register cron job (C# delegate) |
Schedule(string name, TimeSpan interval, ScriptBlock script, …) | Interval PowerShell job (inline) |
Schedule(string name, string cron, FileInfo script, …) | CRON job from .ps1 file |
Pause(string name) / Resume(string name) | Toggle IsSuspended |
Cancel(string name) | Remove job and stop its loop |
GetSnapshot(TimeZoneInfo? tz = null, bool asHashtable = false, params string[] nameFilter) | Lightweight listing |
GetReport(TimeZoneInfo? tz = null) | Full ScheduleReport |
Types:
JobInfo: Name, LastRunAt, NextRunAt, IsSuspendedScheduleReport: GeneratedAt, Jobs[]
PowerShell cmdlets
Add-KrScheduling— enable schedulerRegister-KrSchedule— create jobs (interval/cron × block/file/code)Suspend-KrSchedule/Resume-KrSchedule— control executionGet-KrScheduleSnapshot— quick listGet-KrScheduleReport— aggregated report
Best practices
- Store timestamps internally in UTC — consistent math & comparisons
- Use
WaitAsync(token)when invoking PowerShell — fast shutdowns - Return
Taskfrom C# jobs — avoid thread-pool starvation - Keep runspace pools small (
Options.MaxSchedulerRunspaces) — jobs are usually light - Log failures inside job bodies — a failed job should never crash the server
- Pause instead of delete for outages — preserves next run predictability
Return to the Guides index.