All Products
Search
Document Center

PolarDB:pg_cron

Last Updated:Mar 27, 2026

pg_cron is a third-party PostgreSQL extension supported by PolarDB for PostgreSQL (Compatible with Oracle). It lets you schedule SQL statements using standard cron syntax — no external scheduler or additional tooling required.

Prerequisites

Before you begin, make sure you have:

  • A PolarDB for PostgreSQL (Compatible with Oracle) cluster running one of the following revision versions:

    • Version 2.0: revision version 2.0.14.1.0 or later

    • Version 1.0: revision version 1.1.1 or later

To check your current revision version, run:

SHOW polar_version;

Usage notes

If your cluster runs minor engine version 2.0.14.16.0 or earlier, restarting the cluster changes the cluster port, which causes pg_cron jobs to fail. Upgrade to minor engine version 2.0.14.16.1 or later to avoid this issue.

How it works

Scheduled task storage

image

All jobs are stored in the cron.job table. When the database starts, pg_cron builds a JOB LIST and TASK LIST from this table. The cron.job_cache_invalidate trigger keeps both lists in sync whenever you create, modify, or delete a job.

Task execution states

image

Each task moves through the following states during execution:

StateDescription
WAITINGDefault state. Skips if conditions are not met (task inactive or schedule not reached); transitions to START when conditions are met.
STARTEstablishes the connection and runs a connection test. Transitions to CONNECTING on success, or ERROR on failure.
CONNECTINGVerifies that the task is active and the connection is healthy. Transitions to SENDING or ERROR.
SENDINGSends the task to the PolarDB for PostgreSQL (Compatible with Oracle) server. Transitions to RUNNING or ERROR.
RUNNINGWaits for the result. Transitions to DONE on success, or ERROR if the task becomes inactive or the connection drops.
ERRORTask failed. Resets the connection and transitions to DONE.
DONETask complete. Resets task state and transitions back to WAITING.

Functions

Only privileged accounts can call the following functions to manage jobs. Standard accounts have read-only access to the cron.job table.
All jobs are stored in the postgres database. Connect to postgres to manage jobs. Schedules run in Greenwich Mean Time (GMT). You must convert your local time into GMT.

cron.schedule — Create a job

cron.schedule(
    job_name TEXT,
    schedule TEXT,
    command  TEXT
);
ParameterDescription
job_nameName of the job. Optional — leave blank to skip.
scheduleA standard cron expression.
commandThe SQL statement to run.

cron.schedule_in_database — Create a job in a specific database

cron.schedule_in_database(
    job_name TEXT,
    schedule TEXT,
    command  TEXT,
    db_name  TEXT
);
ParameterDescription
job_nameName of the job.
scheduleA standard cron expression.
commandThe SQL statement to run.
db_nameThe database in which the job runs.

cron.alter_job — Modify a job

cron.alter_job(
    job_id   BIGINT,
    schedule TEXT    DEFAULT NULL,
    command  TEXT    DEFAULT NULL,
    db_name  TEXT    DEFAULT NULL,
    active   BOOLEAN DEFAULT NULL
);
ParameterDescription
job_idID of the job to modify.
scheduleNew cron expression. Pass NULL to leave unchanged.
commandNew SQL statement. Pass NULL to leave unchanged.
db_nameNew target database. Pass NULL to leave unchanged.
activeSet to true to enable or false to disable the job. Pass NULL to leave unchanged.

cron.unschedule — Delete a job

cron.unschedule(job_id   BIGINT);
cron.unschedule(job_name TEXT);
ParameterDescription
job_idID of the job to delete.
job_nameName of the job to delete.

Get started

The following operations require a privileged account.

Step 1: Install the extension

Connect to your database and run:

CREATE EXTENSION pg_cron;

Step 2: Create a job

The following example creates a job named task1 that runs SELECT 1 every minute against the db01 database. The returned value is the job ID.

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

Expected output:

 schedule_in_database
----------------------
                    1
(1 row)

Step 3: View scheduled jobs

SELECT * FROM cron.job;

Expected output:

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

Step 4: Check job execution history

SELECT * FROM cron.job_run_details;

Expected output:

 jobid | runid | job_pid | database | username | command  |  status   | return_message |          start_time           |           end_time
-------+-------+---------+----------+----------+----------+-----------+----------------+-------------------------------+-------------------------------
     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
     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
(2 rows)

To filter for failed jobs only:

SELECT * FROM cron.job_run_details WHERE status = 'failed';

The return_message column contains the error details for each failed run.

Step 5: Delete a job

SELECT cron.unschedule('task1');

Expected output:

 unschedule
------------
 t
(1 row)

Remove the extension

DROP EXTENSION pg_cron;

Schedule examples

pg_cron uses standard cron syntax. The five fields map to:

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

All schedules run in GMT.

Delete expired data every week

SELECT cron.schedule(
    'weekly-cleanup',
    '30 3 * * 6',
    $$DELETE FROM events WHERE event_time < now() - interval '1 week'$$
);

Runs at 03:30 GMT every Saturday.

Run VACUUM daily

SELECT cron.schedule('daily-vacuum', '0 10 * * *', 'VACUUM;');

Runs at 10:00 GMT every day.

Purge job history to prevent table bloat

The cron.job_run_details table accumulates a row for every job execution and can grow large over time. Schedule a regular cleanup job to keep it manageable:

SELECT cron.schedule(
    'purge-job-history',
    '0 0 * * *',
    $$DELETE FROM cron.job_run_details WHERE end_time < now() - interval '7 days'$$
);

Run a job every minute

SELECT cron.schedule('every-minute', '* * * * *', 'SELECT 1;');

Run a job at a specific minute each hour

SELECT cron.schedule('hourly-at-23', '23 * * * *', 'SELECT 1;');

Run a job on a specific day of the month

SELECT cron.schedule('monthly-on-4th', '* * 4 * *', 'SELECT 1;');