How to enable pg_cron

vmk8s

The pg_cron extension runs SQL jobs on a schedule. It is disabled by default.

Enable pg_cron

juju config postgresql plugin-pg-cron-enable=true
juju config postgresql-k8s plugin-pg-cron-enable=true

Wait for all units to show active and idle in juju status.

The charm installs pg_cron only in the postgres database. Jobs use background workers and schedules use GMT. The charm does not grant application users permission to schedule jobs.

pg_cron shares worker slots with other extensions, parallel queries, and logical replication. Small deployments may need a higher cpu-max-worker-processes value to accommodate the scheduler, concurrent jobs, and other workers. Enabling pg_cron does not increase this limit automatically.

Connect to PostgreSQL

Find the secret labelled database-peers.postgresql.app (VM) or database-peers.postgresql-k8s.app (K8s):

juju secrets --format=yaml

Reveal the secret and use its operator-password value when psql prompts for a password. Replace <secret-id> with the secret’s ID:

juju show-secret <secret-id> --reveal

Find the PostgreSQL primary and its address. Replace <primary-unit> with the unit returned by get-primary, then open its shell:

juju run postgresql/leader get-primary
juju status postgresql
juju ssh <primary-unit> bash
juju run postgresql-k8s/leader get-primary
juju status postgresql-k8s
juju ssh --container postgresql <primary-unit> bash

In that shell, connect to postgres as operator. Replace <primary-address> with the primary unit’s address from juju status:

psql --host=<primary-address> --username=operator --password postgres

Schedule and inspect jobs

For example, to record a timestamp every minute:

CREATE TABLE public.cron_example (run_at timestamptz DEFAULT now());
SELECT cron.schedule(
    'record-time', '* * * * *',
    'INSERT INTO public.cron_example DEFAULT VALUES'
);

After a minute, check the table and job history:

SELECT * FROM public.cron_example;
SELECT jobid, jobname, schedule, database, active FROM cron.job;
SELECT jobid, status, return_message, start_time
FROM cron.job_run_details ORDER BY start_time DESC LIMIT 10;

To target an existing application database, call cron.schedule_in_database() from postgres. Replace application_db with the database name:

SELECT cron.schedule_in_database(
    'vacuum-application', '0 3 * * *', 'VACUUM', 'application_db'
);

Jobs run with the scheduling user’s permissions. Job definitions are replicated, and scheduling resumes on the new primary after a switchover. Standbys do not execute jobs.

Pause or remove a job

To pause a job while keeping its definition and history:

SELECT cron.alter_job(
    (SELECT jobid FROM cron.job WHERE jobname = 'record-time'), active := false
);

Use active := true to resume it.

To remove its schedule:

SELECT cron.unschedule('record-time');

Disable pg_cron

Disabling pg_cron drops the extension, including all job definitions and run history. Re-enabling it does not restore them. Pause jobs instead if you need to keep this data.

juju config postgresql plugin-pg-cron-enable=false
juju config postgresql-k8s plugin-pg-cron-enable=false

Wait for all units to show active and idle. If database objects depend on the extension, the charm reports blocked; remove those dependencies before disabling it.