Schedule tasks
Create task runs from one-time, interval, or cron schedules.
Use a task-colocated schedule when Runlane should create runs at known times without an application request calling trigger().
Add a schedule to the task
Put the schedule beside its task definition. For example, create src/tasks/generate-daily-report.ts:
import { task } from '@runlane/core'
import * as z from 'zod'
const env = z.object({ REPORTS_API_URL: z.url() }).parse(process.env)
export const generateDailyReport = task({
id: 'report.generate-daily',
schema: z.object({ accountId: z.string() }),
schedule: {
id: 'report.daily.account-123',
every: '24h',
startsAt: new Date('2026-01-01T09:00:00.000Z'),
payload: { accountId: 'account-123' },
},
async run({ accountId }, context) {
const response = await fetch(new URL(`/accounts/${encodeURIComponent(accountId)}/daily`, env.REPORTS_API_URL), {
method: 'POST',
signal: context.signal,
})
if (!response.ok) throw new Error(`Reports API returned ${response.status}`)
},
})This task creates one run every 24 hours from the given start time.
Register the task in createRunlane({ tasks }). Schedule ids must be unique inside the runtime. Registration validates and transforms the schedule payload synchronously through the task schema. Due occurrences use that normalized payload without applying the input schema again.
A schedule accepts one cadence: runAt for one occurrence, every for a fixed interval, or cron for cron-based occurrences. Do not mix cadence fields in one schedule.
Let maintenance create due runs
Schedules do nothing until a maintenance pass claims due occurrences. If you run bounded maintenance, call it from the scheduled job or command that owns maintenance:
import { type RunlaneRuntime } from '@runlane/core'
export async function materializeDueSchedules(runlane: RunlaneRuntime) {
await runlane.start()
const maintenance = await runlane.runMaintenanceOnce()
return maintenance.materialized.map((item) => item.run.id)
}The example returns the run ids created by that pass.
Maintenance creates queued runs and delivery requests. It does not execute task handlers, so the deployment also needs a worker or transport consumer.
Run maintenance from one or more supervised processes or scheduled invocations. Storage claims occurrences so overlapping maintenance calls do not intentionally create the same occurrence twice.
Occurrence claims are temporary and use storage transaction time. A later pass can reclaim an expired occurrence and reuse its deterministic run.
An active claim or completed occurrence is not duplicated.
To verify the setup, choose a schedule that is due in an isolated environment, run one maintenance pass, and confirm maintenance.materialized contains its schedule id. Then run a drain worker and confirm the new run succeeds.
If no run appears, check the task registration, schedule state, due time, runtime clock, and environment.
If the run is queued but does not execute, fix the worker or delivery path. Adding another schedule will create another source of work.
Assign maintenance ownership before deploying schedules.