# How do I run scheduled (cron) tasks?

> Every SupaHost project has pg_cron built in. Schedule SQL with cron.schedule or the dashboard's Cron tab, call Edge Functions on a timer, and check run history.

Source: https://supahost.app/kb/scheduled-tasks · Updated 2026-11-11

Use pg_cron, which is built into every SupaHost project. Call `cron.schedule('job-name', '<schedule>', $$<sql>$$)` in the SQL editor, or create the job in Dashboard → your project → **Cron** tab → **New job**. You don't need to install anything: don't run `create extension pg_cron`.

## Example: nightly cleanup for a bookings app

```sql
select cron.schedule(
  'expire-holds',
  '15 2 * * *',          -- 02:15 UTC every day
  $$ delete from public.booking_holds where created_at < now() - interval '1 day' $$
);
```

Check it's there and see how runs went:

```sql
select jobid, jobname, schedule, active from cron.job;

select jobid, status, return_message, start_time, end_time
from cron.job_run_details
order by start_time desc
limit 20;
```

Remove it with:

```sql
select cron.unschedule('expire-holds');
```

## Schedules

| Schedule | Meaning |
|---|---|
| `* * * * *` | every minute |
| `*/5 * * * *` | every 5 minutes |
| `0 * * * *` | hourly, on the hour |
| `0 3 * * *` | daily at 03:00 UTC |
| `0 9 * * 1` | Mondays at 09:00 UTC |
| `30 seconds` | every 30 seconds |

All schedules are in UTC. The Cron tab offers presets (Every minute, Every 5 minutes, Hourly, Daily at 03:00 UTC, Every 30 seconds) and example SQL.

## Calling code on a schedule

To run JavaScript or TypeScript on a timer, deploy an Edge Function and have a cron job call it with `net.http_post` (see [How do I run background jobs?](/kb/background-jobs) for a full example with the key stored in Vault).

## How cron works on SupaHost

- Jobs run as the `postgres` role inside your project's database.
- Job names are lowercase letters, digits, `-` and `_`, up to 49 characters.
- The `cron` schema in your project provides `cron.schedule`, `cron.unschedule`, `cron.job` and `cron.job_run_details` (last 1000 runs). Other pg_cron functions, such as `cron.alter_job`, aren't available: to change a job, unschedule it and schedule it again, or use **Edit** in the Cron tab.
- Each job has an **Active** toggle in the Cron tab, and **Recent runs** shows the last 50 runs.
- Run history older than 14 days is deleted automatically.

## FAQ

### Why does `create extension pg_cron` fail?

pg_cron is already set up for every project, and the extension itself can only be created in the instance's main database. Skip that step and call `cron.schedule` directly. See [ERROR: can only create extension in database postgres](/kb/create-extension-pg-cron).

### What time zone do jobs use?

UTC. Convert your local time when you write the schedule.

### Are cron jobs copied to branches or by the Supabase import?

No. Branches and the one-click import from Supabase Cloud don't copy cron jobs; recreate them in the new project.

## Related

- [Scheduled jobs with pg_cron](/docs/cron-queues/cron)
- [Cron syntax and examples](/docs/cron-queues/cron-syntax)
- [Monitoring and debugging cron jobs](/docs/cron-queues/monitoring-jobs)
- [Cron job not running](/kb/cron-job-not-running)
