Cron and interval timers
Schedule background work with the timer block, including cron and duration intervals, jitter, and overlap protection.
- 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/2jitter on top of either form. Each tick fires anywhere within a window of widthrandomaround 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
@executepipeline. - 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:
- second
- minute
- hour
- day-of-month
- month
- day-of-week
- year
Examples:
| Expression | Meaning |
|---|---|
"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>:
| Counter | Meaning |
|---|---|
starts | Incremented at the start of every executed tick. |
skipped | Incremented when a tick is dropped due to overlap protection. |
errors | Incremented when the pipeline returns an error. |
total_execution_micros | Cumulative 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.
Related
- timer - reference for the timer block.
- interval - shared schedule syntax.
- Duration Units - accepted suffixes.
- Execution pipelines - what runs
inside
@execute.
- 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
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
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