Radiator Server Documentation — v10.33.4

Cron and interval timers

Schedule background work with the timer block, including cron and duration intervals, jitter, and overlap protection.

Table of Contents
  • Quick reference
  • Anatomy of a timer
  • Duration intervals
  • Cron intervals
  • Time zone and daylight saving
  • Avoiding thundering herds
  • Overlap protection
  • Counters and observability
  • Common patterns
  • Periodic backend health probe
  • Daily report at 02:00
  • Spread maintenance across the hour
  • Many fast timers
  • Behavior at startup and shutdown
  • Related

The timer block runs a pipeline on a schedule. Use timers for periodic maintenance, polling external systems, warming caches, exporting reports, and similar background work that is not driven by an inbound request handled by the servers block.

This article explains how the two scheduling modes behave and how to choose between them, and gives example configurations for common patterns.

Quick reference

  • interval <duration> - fire every <duration>. Simple and good for "every few minutes" schedules.
  • interval "<cron expression>" - fire on every wall-clock instant matching the seven-field cron expression. Good for "every day at 02:00" or "every 15 minutes on the hour".
  • random <duration> - apply +/- random/2 jitter on top of either form. Each tick fires anywhere within a window of width random around the scheduled time. Use this to protect shared backends from simultaneous load when several Radiator instances, or several timers in one instance, share the same schedule.

The first tick fires after one full interval, not at configuration load. For one-shot work at startup or reload, use @init.

Anatomy of a timer

timer "CACHE_REFRESH" {
    interval 5m;
    @execute {
        backend "REFRESH_CACHE";
    }
}

Behavior:

  • A scheduler task starts when the configuration is loaded.
  • It computes the next scheduled instant (now + 5 minutes), sleeps until then, and runs the @execute pipeline.
  • After the pipeline is dispatched, the scheduler computes the next scheduled instant based on the intended start time of current instant.

Duration intervals

The next tick is anchored to the previous planned instant plus the configured <duration> (then offset by +/- random/2 if random is configured), so the time between ticks does not drift over time. If a tick takes longer than <duration>, missed slots are skipped. The next fire lands on the next future slot rather than bursting to catch up.

The schedule does not lock to a wall-clock boundary. It is based entirely on Radiator start up time. If you need wall clock alignment use a cron expression instead.

Duration intervals are best when:

  • The work is periodic maintenance such as cache refreshes, cleanup sweeps, or health probes that should fire at roughly regular intervals but do not need to land on a specific clock time.
  • The interval is short (sub-minute) and a cron expression would be awkward.
  • You want to avoid synchronized bursts on shared backends. Cron timers across many Radiator instances tend to pile up on the top of the minute or hour; duration intervals naturally spread out because each instance starts its clock when the configuration loads.

Cron intervals

Cron intervals are evaluated against the current wall clock on every loop iteration, so the schedule for "every 15 minutes on the hour" stays aligned to xx:00, xx:15, xx:30, xx:45 indefinitely.

Radiator uses a seven-field cron expression. The fields are:

  1. second
  2. minute
  3. hour
  4. day-of-month
  5. month
  6. day-of-week
  7. year

Examples:

ExpressionMeaning
"0 0 * * * * *"Top of every minute (second 0).
"0 */5 * * * * *"Every 5 minutes on the minute.
"0 0 2 * * * *"Daily at 02:00:00.
"0 0 0 * * Mon *"Mondays at midnight.
"0 30 9-17 * * Mon-Fri *"At minute 30 of every hour 09-17 on weekdays.

Cron intervals are best when:

  • The work must align with wall-clock boundaries.
  • The schedule is sparse (per minute, hour, day).
  • Operators expect to read the schedule at a glance.

Time zone and daylight saving

Cron expressions are evaluated in the host's local time zone by default, so a timer set for "0 0 2 * * * *" fires at 02:00 local year-round and follows whatever DST rules the host observes. Set time-zone inside the interval block to pin the schedule to a specific zone instead:

timer "DAILY_UTC" {
    interval {
        cron "0 0 2 * * * *";
        time-zone "UTC";
    }
    @execute {
        backend "REPORT";
    }
}

timer "DAILY_HELSINKI" {
    interval {
        cron "0 0 2 * * * *";
        time-zone "Europe/Helsinki";
    }
    @execute {
        backend "REPORT";
    }
}

time-zone accepts the literal "local" (the same as omitting it) or any IANA tz database name ("UTC", "Europe/Helsinki", "America/New_York", and so on). Two consequences of standard cron semantics apply when the chosen zone observes DST (daylight savings time):

  • Spring forward: a time that does not exist in the zone (for example 02:30 on the day clocks jump from 02:00 to 03:00) is skipped for that day.
  • Fall back: a time that occurs twice fires once, on the first occurrence.

Using time-zone "UTC" is often helpful to ensure a time zone without DST.

Avoiding thundering herds

Cron schedules fire on the same wall-clock instant on every Radiator instance. If many timers - or many instances - share a backend, they hit it at the same moment. Add random jitter to spread the load:

timer "POLL_TENANT_A" {
    interval {
        cron "0 0 * * * * *";
        random 10s;
    }
    @execute {
        backend "TENANT_A_POLLER";
    }
}

Each tick now fires somewhere in a 10-second window (+/- 5s) around the top of the minute.

Overlap protection

A timer runs at most one instance of its @execute pipeline at a time. If a new tick fires while the previous run is still going, the new tick is dropped and the skipped counter is incremented.

Counters and observability

Each timer publishes the following counters under timer::<NAME>:

CounterMeaning
startsIncremented at the start of every executed tick.
skippedIncremented when a tick is dropped due to overlap protection.
errorsIncremented when the pipeline returns an error.
total_execution_microsCumulative pipeline execution time in microseconds.

total_execution_micros is a 64-bit SNMP-style counter and wraps at 2^64. Average run cost between two samples is (total_execution_micros_b - total_execution_micros_a) / (starts_b - starts_a).

A process-wide gauge timer::count reports the number of timers currently registered.

Every tick logs under namespace timer::<NAME> and includes a timer = <NAME> field, so log lines and counter samples can be correlated by namespace.

Common patterns

Periodic backend health probe

Probe a backend every 30 seconds and let the result update its counters.

timer "HEALTH_PROBE" {
    interval 30s;
    @execute {
        backend "AUTH_BACKEND_HEALTH";
    }
}

Daily report at 02:00

Generate a report once per day, jittered so multiple Radiator instances do not all start at the same instant.

timer "DAILY_REPORT" {
    interval {
        cron "0 0 2 * * * *";
        random 60s;
    }
    @execute {
        backend "REPORT_GENERATOR";
    }
}

Spread maintenance across the hour

Run a maintenance step once per hour, with each replica picking a different second within a 60-second window.

timer "HOURLY_MAINTENANCE" {
    interval {
        cron "0 0 * * * * *";
        random 1m;
    }
    @execute {
        backend "MAINTENANCE";
    }
}

Many fast timers

Scheduling hundreds of timers is supported. Keep the @execute body efficient, since a slow body causes ticks to be skipped:

timer "POLL_DEVICE_001" {
    interval 100ms;
    @execute {
        backend "POLL_001";
    }
}

Behavior at startup and shutdown

  • Startup: a scheduler task starts when the timer is registered. The first tick fires after one full interval (plus or minus jitter).
  • Shutdown: all scheduler tasks and any in-flight pipeline payloads are aborted. A timer pipeline mid-run does not block shutdown.
Navigation
  • About Radiator software development security

  • Architecture Overview

  • Backend Load Balancing

  • Basic Installation

  • Built-in Environment Variables

  • Byte Size Units

  • Certificate Revocation Lists

  • Comparison Operators

  • Configuration Editor

  • Configuration Import and Export

  • Containers

  • Cron and interval timers

  • Data Types

  • Duration Units

  • Environment Variables

  • Execution Context

  • Execution Pipelines

  • Filters

  • Getting a Radiator License

  • Health check /live and /ready

  • High Availability and Load Balancing

  • High availability identifiers

  • HTTP Basic Authentication

  • Introduction

  • Linux systemd support

  • Local AAA Backends

  • Log storage and formatting

  • Management API privilege levels

  • Namespaces

  • Password Hashing

  • Password Rehashing During Login

  • Probabilistic Sampling

  • Prometheus scraping

  • PROXY Protocol Support

  • Radiator server health and boot up logic

  • Radiator sizing

  • Radiator software releases

  • RadiatorDB

  • RadiatorDB Backup

  • RadiatorDB CLI

  • RadiatorDB Installation

  • RadiatorDB REST API

  • Rate Limiting

  • Rate Limiting Algorithms

  • Reverse Dynamic Authorization

  • Service Level Objective

  • TACACS+ Authentication, Authorization, and Accounting

  • Template Rendering CLI

  • Timestamp Format

  • Timestamps

  • Tools radiator-client

  • TOTP/HOTP Authentication

  • What is Radiator?

  • YubiKey Authentication

  • YubiKey Context Variables