All Products
Search
Document Center

PolarDB:pg_cron

Last Updated:Mar 28, 2026

pg_cron is a third-party extension for PolarDB for PostgreSQL that lets you schedule SQL statements using standard cron expressions. Define recurring database tasks—backups, report generation, data cleanup—entirely in SQL, without external schedulers or custom scripts.

Prerequisites

pg_cron is supported on PolarDB for PostgreSQL clusters running:

  • PostgreSQL 14, revision version 14.9.14.0 or later

  • PostgreSQL 11, revision version 1.1.1 or later

To check your revision version, run one of the following:

-- PostgreSQL 14
SELECT version();

-- PostgreSQL 11
SHOW polar_version;

Known issue: port change on cluster restart

On clusters running minor engine version 14.10.16.0 or earlier, restarting the cluster changes the cluster port, which causes pg_cron tasks to fail. This issue is fixed in minor engine version 14.10.16.1 and later.

How it works

Scheduled task storage

image

All scheduled tasks are stored in the cron.job table. When the database starts, pg_cron builds an in-memory JOB LIST and TASK LIST from this table. The cron.job_cache_invalidate trigger keeps these lists synchronized with cron.job in real time whenever tasks are added, modified, or removed.

Task execution lifecycle

image

Each task moves through the following states:

StateDescription
WAITINGDefault state. The task waits until its scheduled time and activation conditions are met.
STARTEstablishes connection information and runs a connection test. On success, moves to CONNECTING. On failure, moves to ERROR.
CONNECTINGVerifies activation status and connection health. On success, moves to SENDING. On failure, moves to ERROR.
SENDINGSends the task to the PolarDB for PostgreSQL server. On success, moves to RUNNING. On failure, moves to ERROR.
RUNNINGWaits for the task result. On success, moves to DONE. On failure, moves to ERROR.
ERRORTask failed. Resets connection information, then moves to DONE.
DONETask complete. Resets task information, then returns to WAITING.

Permissions

Only privileged accounts can create, modify, and delete scheduled tasks. Standard accounts have read-only access to the cron.job table.

All scheduled tasks are stored in the postgres database. Connect to postgres to manage tasks.

All tasks run on Greenwich Mean Time (GMT). Convert your local time to GMT before setting a schedule.

Enable pg_cron

Connect to the postgres database and run:

CREATE EXTENSION pg_cron;

To remove the extension:

DROP EXTENSION pg_cron;

Manage scheduled tasks

Create a task

Use cron.schedule to create a task in the current database, or cron.schedule_in_database to target a specific database.

`cron.schedule`

SELECT cron.schedule(
    'job_name',   -- Task name (optional)
    'schedule',   -- Cron expression
    'command'     -- SQL statement to run
);
-- Returns: jobid (BIGINT)
ParameterTypeDescription
job_nameTEXTTask name. Optional; leave blank to create an unnamed task.
scheduleTEXTCron expression defining when the task runs.
commandTEXTSQL statement to execute.

`cron.schedule_in_database`

SELECT cron.schedule_in_database(
    'job_name',   -- Task name
    'schedule',   -- Cron expression
    'command',    -- SQL statement to run
    'db_name'     -- Target database
);
-- Returns: jobid (BIGINT)
ParameterTypeDescription
job_nameTEXTTask name.
scheduleTEXTCron expression defining when the task runs.
commandTEXTSQL statement to execute.
db_nameTEXTDatabase in which the task runs.

Example: Create a task named task1 that runs SELECT 1 every minute in database db01.

SELECT cron.schedule_in_database('task1', '* * * * *', 'SELECT 1', 'db01');
 schedule_in_database
----------------------
                    1
(1 row)

The return value is the task ID (jobid).

View tasks

SELECT * FROM cron.job;
 jobid | schedule  | command  | nodename | nodeport | database | username | active | jobname
-------+-----------+----------+----------+----------+----------+----------+--------+---------
     1 | * * * * * | SELECT 1 | /tmp     |    39361 | db01     | u1       | t      | task1
(1 row)

Modify a task

SELECT cron.alter_job(
    job_id   => 1,              -- Task ID (required)
    schedule => '0 10 * * *',  -- New schedule (optional)
    command  => 'VACUUM;',     -- New SQL statement (optional)
    db_name  => 'db01',        -- New target database (optional)
    active   => true           -- Enable or disable the task (optional)
);
ParameterTypeDescription
job_idBIGINTID of the task to modify. Required.
scheduleTEXTNew cron expression. Omit to keep the current value.
commandTEXTNew SQL statement. Omit to keep the current value.
db_nameTEXTNew target database. Omit to keep the current value.
activeBOOLEANSet to true to enable or false to disable the task. Omit to keep the current value.

Pass only the parameters you want to change. Omitted parameters keep their current values.

Delete a task

-- Delete by task ID
SELECT cron.unschedule(1);

-- Delete by task name
SELECT cron.unschedule('task1');
 unschedule
------------
 t
(1 row)

View execution history

SELECT * FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10;
 jobid | runid | job_pid | database | username | command  |  status   | return_message |          start_time           |           end_time
-------+-------+---------+----------+----------+----------+-----------+----------------+-------------------------------+-------------------------------
     1 |     2 | 4152438 | db01     | u1       | SELECT 1 | succeeded | 1 row          | 2023-10-19 03:56:00.006468+00 | 2023-10-19 03:56:00.006822+00
     1 |     1 | 4152316 | db01     | u1       | SELECT 1 | succeeded | 1 row          | 2023-10-19 03:55:00.020442+00 | 2023-10-19 03:55:00.021512+00
(2 rows)

The status column has three possible values:

StatusDescription
runningTask is currently executing.
succeededTask completed successfully.
failedTask failed. Check return_message for the error detail.

To filter for failed tasks:

SELECT * FROM cron.job_run_details WHERE status = 'failed' ORDER BY start_time DESC;

Cron expression reference

pg_cron uses the standard cron expression format:

┌───────────── Minute (0–59)
│ ┌────────────── Hour (0–23)
│ │ ┌─────────────── Day of month (1–31)
│ │ │ ┌──────────────── Month (1–12)
│ │ │ │ ┌───────────────── Day of week (0–6, where 0 and 7 = Sunday)
│ │ │ │ │
* * * * *

Common schedule patterns:

ExpressionSchedule
* * * * *Every minute
*/5 * * * *Every 5 minutes
23 * * * *Every hour at minute 23
0 10 * * *Daily at 10:00 GMT
0 0 * * 1-5Weekdays at midnight GMT
30 3 * * 6Saturdays at 03:30 GMT
* * 4 * *Every minute on the 4th of each month

Examples:

-- Run VACUUM every day at 10:00 GMT
SELECT cron.schedule('nightly-vacuum', '0 10 * * *', 'VACUUM;');

-- Delete events older than one week every Saturday at 03:30 GMT
SELECT cron.schedule('weekly-cleanup', '30 3 * * 6',
    $$DELETE FROM events WHERE event_time < now() - interval '1 week'$$);

-- Run a query every minute
SELECT cron.schedule('heartbeat', '* * * * *', 'SELECT 1;');