Cron jobs
Run a function, call an HTTPS endpoint or run SQL on a schedule, in your own time zone, with retries, overlap control and a run history.
A cron job runs something on a schedule: a deployed function, an HTTPS request to any public URL, or one SQL statement on your database. Each run is a durable background job with retries, a timeout and a history (succeeded, failed, skipped, missed), so a job never runs twice for the same time even when several servers see it due.
#Schedule expressions
Five fields — minute hour day-of-month month day-of-week — or a macro. The console and sov functions schedules preview show the next runs before you save.
| Expression | Runs |
|---|---|
*/5 * * * * | Every 5 minutes |
@hourly (0 * * * *) | At the start of every hour |
0 9 * * MON-FRI | At 09:00 on weekdays |
30 2 1 * * | At 02:30 on the 1st of every month |
0 9 1 * MON | At 09:00 on the 1st and on every Monday (day of month and day of week together mean either) |
@daily, @weekly, @monthly, @yearly | Midnight every day, Sunday, 1st, 1 January |
- Lists
1,15, ranges1-5, steps*/10,10-50/20and5/15(from 5, every 15); namesJAN–DECandSUN–SAT;7is Sunday too. - Dates that never happen (
0 0 30 2 *) are refused when you save; rare ones (0 0 29 2 *) are fine. - Time zones: pick any IANA zone (
Asia/Kolkata,Europe/London,America/New_York); the default is UTC. Daylight saving is handled: a time skipped when clocks go forward runs once just after the change (02:30 runs at 03:30), a time that happens twice runs once — except expressions with*in the hour field, which keep their interval through the repeated hour.
#Create a cron job
sov functions schedules preview '0 9 * * MON-FRI' --tz Asia/Kolkata
sov functions schedules create '0 3 * * *' -f cleanup --tz Asia/Kolkata --overlap skip --name "Nightly cleanup"
sov functions schedules create @hourly --url https://hooks.example.com/ping -H 'Authorization: @secret:HOOK_TOKEN'
sov functions schedules create '*/15 * * * *' --sql 'SELECT refresh_leaderboard()' --timeout 20
sov functions schedules run <schedule-id> # run it now
sov functions schedules runs <schedule-id> # history#Targets
| Target | Settings | How it runs |
|---|---|---|
| Function | function_name, payload (the event) | Like a background call. context.caller.role is "schedule"; context.schedule has id, name, scheduled_at, manual, attempt; context.idempotency_key is sched:<id>:<slot>. |
| HTTPS | url, method, headers, body, timeout_sec | Leaves through the egress proxy: public host names only (private, loopback and metadata addresses are refused). A non-2xx answer is a failed try; the status and the first 2 KB of the answer are kept. |
| SQL | sql, params, key_secret | One SELECT / INSERT / UPDATE / DELETE (call a database function with SELECT my_job()) through your project's SQL API with the service-role key stored as the secret key_secret (default SOVARIA_SERVICE_ROLE_KEY). It runs as your project's database role — never a superuser — with statement_timeout set to the job's timeout. |
#Retries, timeouts and overlap
- Tries per run (
max_attempts, default 3, up to your plan's tries per event): a failed try is retried with backoff; the last failure is kept under Dead letters. - Timeout (
timeout_sec): lowers the run's limit below the function's or plan's; for SQL it is the statement timeout. - Overlap —
allow(default) runs every slot;skipskips a slot while the previous run is still going and logs it as Skipped. - Run now queues one run immediately without changing the regular times (it follows the overlap rule).
- Slots missed while the scheduler was down are logged once as Missed, and the latest one runs late — they are not replayed one by one.
#Secrets
Never put a key in a payload, header or body: write {"$secret": "NAME"} instead. The value is filled in when the job runs (from the function's secrets for functions, the project and environment secrets for HTTPS and SQL), and logs show only the name. Plain values under names like password, token or api_key are shown as [redacted]; sending [redacted] back on an edit keeps what is stored. Payloads are at most 256 KB.
#Environments and the kill switch
A schedule belongs to the environment it was made in and runs that environment's function; staging never lists or changes production's schedules. Deleting a function removes its schedules in that environment only. If Sovaria support switches background runs off for an organization (abuse, a runaway loop), its slots are skipped and logged until they are switched back on.
#Compared with Supabase Cron
| Sovaria | Supabase Cron (pg_cron + pg_net) | |
|---|---|---|
| Targets | Function, HTTPS, SQL | SQL, database function, HTTP, Edge Function |
| Time zones | Any IANA zone, DST-aware | UTC only |
| Retries and timeouts | Per job, with dead letters | None built in |
| Overlap control | skip per job | None |
| Run now, preview, readable summary | Yes | No |
| History | Status incl. skipped and missed, duration, tries, response preview, logs | Status and output |
| Secrets | $secret references, redacted in logs | Vault lookups in SQL |
#Plans
| Free | Pro | Ultra | Team | Enterprise | |
|---|---|---|---|---|---|
Cron jobs per project (fn.cron_jobs) | 2 | 25 | 100 | 500 | 2,000 |
Tries per run (fn.trigger_max_attempts) | 3 | 8 | 10 | 12 | 15 |
Each run counts as one invocation (fn.invocations_month) and, for functions, its compute. Run history is kept as long as your plan keeps function logs (fn.log_retention_hours). See Plan limits.
#Coming from Firebase or Supabase?
| Platform | What this is called there |
|---|---|
| Firebase | Scheduled functions (onSchedule) and Cloud Scheduler |
| Supabase | Supabase Cron (pg_cron, pg_net) |
| Sovaria | Cron jobs |
Moving an existing app? Migrate from Supabase or Firebase imports users (with their passwords), data, files and rules.