Supabase’s pg_cron is a PostgreSQL extension that lets you schedule SQL commands directly within your database.
Here’s a quick look at pg_cron in action. Imagine you want to clean up old user sessions every night at 3 AM. You’d use a command like this:
SELECT cron.schedule(
'cleanup-sessions',
'0 3 * * *',
$$ DELETE FROM auth.sessions WHERE last_seen < NOW() - INTERVAL '7 days' $$
);
This SELECT statement, when executed against your Supabase project’s database, registers a cron job named cleanup-sessions. The second argument, '0 3 * * *', is a standard cron schedule string meaning "at 3:00 AM every day." The third argument, the DELETE statement, is the actual work the job will perform: removing session records older than a week.
pg_cron is a powerful tool because it moves job scheduling into the database. Traditionally, you might have a separate server or a cloud function running a cron daemon, polling a queue, or triggering scheduled tasks. With pg_cron, the database itself is the scheduler. This means:
- Reduced Latency: Jobs can react to database state changes more directly.
- Simplified Architecture: Fewer external services to manage and monitor.
- Database-Centric Logic: SQL-based jobs naturally fit within a database-centric application.
The core of pg_cron is its cron schema, which provides functions to manage jobs. The primary functions you’ll interact with are:
cron.schedule(job_name, cron_schedule, command): This is for creating or updating a job. If a job withjob_namealready exists, it will be updated.cron.unschedule(job_name): This removes a scheduled job.cron.schedule_in_timezone(job_name, cron_schedule, command, timezone): Similar toschedule, but allows you to specify a timezone for the schedule.cron.job(job_id): Retrieves information about a specific job.cron.jobs(): Lists all scheduled jobs.
When you run cron.schedule, pg_cron registers the job and ensures it’s executed according to the schedule. It leverages PostgreSQL’s background worker processes to achieve this. A dedicated pg_cron worker wakes up periodically, checks the schedule, and executes any jobs that are due.
The cron_schedule uses the familiar cron syntax:
* * * * *(minute, hour, day of month, month, day of week)0 3 * * *means "at 0 minutes past the 3rd hour, on any day of the month, any month, any day of the week" – i.e., 3:00 AM daily.*/15 * * * *means "every 15 minutes."0 0 1 * *means "at midnight on the 1st of every month."
Supabase projects come with pg_cron pre-installed and enabled. You can access it by connecting to your database using a tool like psql or directly through the SQL Editor in the Supabase dashboard.
The most surprising truth about pg_cron is that it doesn’t actually store the job definitions in a persistent table that you can directly SELECT * FROM. Instead, the job definitions are managed internally by the pg_cron extension itself, and the cron.schedule and cron.unschedule functions are the only programmatic interface for interacting with them. This means if your database restarts, pg_cron automatically reloads its known jobs from its internal state, ensuring continuity without needing an external configuration file or a separate daemon to manage.
The next logical step is understanding how to handle potential job failures and monitor their execution.