❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️
❄️

Timer (Schedule) Trigger

Timer (Schedule) Trigger

Timer (Schedule) Trigger

Run jobs on a schedule to aggregate data, refresh embeddings, or send digests.

BackgroundService with PeriodicTimer

public class Scheduler : BackgroundService
{
    private readonly IServiceProvider _sp;
    public Scheduler(IServiceProvider sp) => _sp = sp;

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        var timer = new PeriodicTimer(TimeSpan.FromMinutes(5));
        while (await timer.WaitForNextTickAsync(ct))
        {
            using var scope = _sp.CreateScope();
            var kernel = scope.ServiceProvider.GetRequiredService<Kernel>();
            await RunJobAsync(kernel, ct);
        }
    }
}
  • Use distributed locks (e.g., SQL/Redis) if running multiple instances.
  • Record last-success and catch up on missed windows after outages.

Alternatives

Linux — cron

Run your console app on a schedule via crontab.

# Edit user crontab
crontab -e

Example entry (every 15 minutes):

*/15 * * * * /usr/bin/dotnet /opt/myapp/MyApp.dll --run-job >> /var/log/myapp/cron.log 2>&1

Minimal console app skeleton:

// Program.cs
using Microsoft.SemanticKernel;

var builder = Kernel.CreateBuilder();
// register plugins and services here
var kernel = builder.Build();

if (args.Contains("--run-job"))
{
    await RunJobAsync(kernel, CancellationToken.None);
}
else
{
    Console.WriteLine("Usage: MyApp --run-job");
}

static async Task RunJobAsync(Kernel kernel, CancellationToken ct)
{
    // your scheduled logic; call SK functions/plugins
    // await kernel.InvokeAsync(...);
}

Notes:

  • Use absolute paths; redirect stdout/stderr to logs.
  • Export required environment variables via /etc/environment or a wrapper script.

Linux — systemd timer

Prefer systemd timers over cron for better observability and unit management.

Service unit (/etc/systemd/system/myapp.service):

[Unit]
Description=MyApp scheduled job

[Service]
Type=oneshot
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/dotnet /opt/myapp/MyApp.dll --run-job
User=myapp
Group=myapp
EnvironmentFile=-/etc/myapp.env

[Install]
WantedBy=multi-user.target

Timer unit (/etc/systemd/system/myapp.timer):

[Unit]
Description=Run MyApp job every 15 minutes

[Timer]
OnCalendar=*:0/15
Persistent=true
Unit=myapp.service

[Install]
WantedBy=timers.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp.timer
sudo systemctl status myapp.timer

Azure Functions — Timer Trigger (C#)

Use a Function with a CRON schedule; integrates with monitoring and scaling.

using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

public static class NightlyJob
{
    // Runs daily at 02:00 UTC
    [FunctionName("NightlyJob")]
    public static async Task Run(
        [TimerTrigger("0 0 2 * * *")] TimerInfo timer,
        ILogger log)
    {
        log.LogInformation($"NightlyJob started at: {System.DateTime.UtcNow:O}");

        // Resolve your Kernel via DI (FunctionsStartup) or a factory
        // var kernel = ...;
        // await kernel.InvokeAsync(...);

        log.LogInformation($"NightlyJob finished at: {System.DateTime.UtcNow:O}");
    }
}

Notes:

  • The CRON format is {second} {minute} {hour} {day} {month} {day-of-week}.
  • For higher reliability, guard against overlaps (e.g., blob lease/distributed lock) and record last-success timestamps.

Pros / Cons

  • Pros: Simple and predictable; good for batch and maintenance jobs.
  • Cons: Overlap/drift and holidays; build guardrails to skip or delay windows.