apps/docs/content/guides/cron/quickstart.mdx
Job names are case sensitive and cannot be edited once created.
Attempting to create a second Job with the same name (and case) will overwrite the first Job.
</Admonition><Tabs scrollable size="small" type="underlined" defaultActiveId="dashboard-schedule-job" queryGroup="database-method"
<TabPanel id="dashboard-schedule-job" label="Dashboard">
Create job button or navigate to the new Cron Job form here.-- Cron Job name cannot be edited
select cron.schedule('permanent-cron-job-name', '30 seconds', 'CALL do_something()');
<Accordion type="default" openBehaviour="multiple" chevronAlign="right" justified size="medium" className="text-foreground-light mt-8 mb-6"
<div className="border-b mt-3 pb-3"> <AccordionItem header="Cron syntax" id="item-1" >
```
┌───────────── min (0 - 59)
│ ┌────────────── hour (0 - 23)
│ │ ┌─────────────── day of month (1 - 31)
│ │ │ ┌──────────────── month (1 - 12)
│ │ │ │ ┌───────────────── day of week (0 - 6) (0 to 6 are Sunday to
│ │ │ │ │ Saturday, or use names; 7 is also Sunday)
│ │ │ │ │
│ │ │ │ │
* * * * *
```
You can use [1-59] seconds (e.g. `30 seconds`) as the cron syntax to schedule sub-minute Jobs.
</AccordionItem>
You can input seconds for your Job schedule interval as long as you're on Postgres version 15.1.1.61 or later.
</Admonition><Tabs scrollable size="small" type="underlined" defaultActiveId="dashboard-edit-job" queryGroup="database-method"
<TabPanel id="dashboard-edit-job" label="Dashboard">
Edit cron job.Save cron job.select cron.alter_job(
job_id := (select jobid from cron.job where jobname = 'permanent-cron-job-name'),
schedule := '*/5 * * * *'
);
Full options for the cron.alter_job() function are:
cron.alter_job(
job_id bigint,
schedule text default null,
command text default null,
database text default null,
username text default null,
active boolean default null
)
It is also possible to modify a job by using the cron.schedule() function by inputting the same job name. This will replace the existing job via upsert.
<Tabs scrollable size="small" type="underlined" defaultActiveId="dashboard-unschedule-job" queryGroup="database-method"
<TabPanel id="dashboard-unschedule-job" label="Dashboard">
Active/Inactive switch next to Job name.-- Activate Job
select cron.alter_job(
job_id := (select jobid from cron.job where jobname = 'permanent-cron-job-name'),
active := true
);
-- Deactivate Job
select cron.alter_job(
job_id := (select jobid from cron.job where jobname = 'permanent-cron-job-name'),
active := false
);
<Tabs scrollable size="small" type="underlined" defaultActiveId="dashboard-delete-job" queryGroup="database-method"
<TabPanel id="dashboard-delete-job" label="Dashboard">
Delete cron job.select cron.unschedule('permanent-cron-job-name');
Unscheduling a Job will permanently delete the Job from cron.job table but its run history remain in cron.job_run_details table.
<Tabs scrollable size="small" type="underlined" defaultActiveId="dashboard-runs-job" queryGroup="database-method"
<TabPanel id="dashboard-runs-job" label="Dashboard">
History button next to the Job name.select
*
from cron.job_run_details
where jobid = (select jobid from cron.job where jobname = 'permanent-cron-job-name')
order by start_time desc
limit 10;
The records in the cron.job_run_details table are not cleaned up automatically. They are also not removed when jobs are unscheduled, which will take up disk space in your database.
Delete old data every Saturday at 3:30AM (GMT):
select cron.schedule (
'saturday-cleanup', -- name of the cron job
'30 3 * * 6', -- Saturday at 3:30AM (GMT)
$$ delete from events where event_time < now() - interval '1 week' $$
);
Vacuum every day at 3:00AM (GMT):
select cron.schedule('nightly-vacuum', '0 3 * * *', 'VACUUM');
Create a hello_world() database function and then call it every 5 minutes:
select cron.schedule('call-db-function', '*/5 * * * *', 'SELECT hello_world()');
To use a stored procedure, you can call it like this:
select cron.schedule('call-db-procedure', '*/5 * * * *', 'CALL my_procedure()');
Make a POST request to a Supabase Edge Function every 30 seconds:
select
cron.schedule(
'invoke-function-every-half-minute',
'30 seconds',
$$
select
net.http_post(
url:='https://project-ref.supabase.co/functions/v1/function-name',
headers:=jsonb_build_object('Content-Type','application/json', 'Authorization', 'Bearer ' || 'YOUR_ANON_KEY'),
body:=jsonb_build_object('time', now() ),
timeout_milliseconds:=5000
) as request_id;
$$
);
This requires the pg_net extension to be enabled.
Be extremely careful when setting up Jobs for system maintenance tasks as they can have unintended consequences.
For instance, scheduling a command to terminate idle connections with pg_terminate_backend(pid) can disrupt critical background processes like nightly backups. Often, there is an existing Postgres setting, such as idle_session_timeout, that can perform these common maintenance tasks without the risk.
Reach out to Supabase Support if you're unsure if that applies to your use case.