Skip to content

Tasks

The Housekeeping scheduled job purges data that CiviCRM creates but never removes. It runs daily and calls the HousekeepingTask.run APIv4 action.

It replaces the core Clean-up Temporary Data and Files job (Job.cleanup): every option of that job is available here as a task, so running both only duplicates work. The first time Housekeeping runs it disables the core job for you, and remembers that it did so - if you disable this extension, the core job is switched back on.

Tasks

Each task is one policy: one thing that gets purged, one way of purging it. The descriptions below also appear on the admin screen.

Things CiviCRM never cleans up

Task Type Default Retention What it does
logFiles logfile On -3 month Deletes rotated log files (CiviCRM.1.<hash>.log.202601011200). CiviCRM renames the log when it passes CIVICRM_LOG_ROTATESIZE or rolls into a new month, but never deletes the renamed files. The live log is never touched.
sqlTriggerFiles file On -3 month Deletes the trigger SQL files (CiviCRM.trigger<id><hash>.sql) written when logging_no_trigger_permission is on. Every rebuild leaves another one. Anything newer than the retention is kept, in case it holds statements nobody has run yet.
liveLogFile logfile Off -1 month Rotates the log currently being written to when nothing has been written to it for longer than the retention. Core only rotates on write, so a quiet site keeps the same file indefinitely. logFiles then removes the result once it is old enough.
systemLog table On -1 year Deletes rows from civicrm_system_log, which records every incoming payment notification and assorted system events. Nothing in core removes them.
mailingSpool table On -1 month Deletes rows from civicrm_mailing_spool - retained copies of outbound mail on sites that spool.
queueItems table On -3 month Deletes items in civicrm_queue_item whose queue no longer exists, so nothing can ever run them. Items belonging to a queue that still exists are never touched, however old.
civicrmLog table Off -1 year Deletes rows from civicrm_log, the change log behind the contact Change Log tab. Off by default because the history is often wanted.
mailingEvents table Off -1 year Deletes per-recipient delivery, open, click, bounce and unsubscribe rows for mailings that finished before the retention - usually the largest tables on a site that mails. Off by default: this permanently destroys the statistics and reports for those mailings. Opt-in and opt-out records (civicrm_mailing_event_subscribe and _confirm) are never touched, and neither is civicrm_mailing_recipients - that is what "include/exclude recipients of prior mailings" reads, so purging it would change who a future mailing goes to. Leave this off if you use the archivemailing extension, which records each mailing's statistics before deleting the same rows: whichever runs first wins, and a mailing this task reached first is archived showing zeros.
detailedLogging table Off -1 year Deletes rows from the log_civicrm_* tables written when Detailed Logging is enabled. Off by default: on some sites that permanent audit trail is the whole point, and there may be a legal requirement to keep it. Does nothing if Detailed Logging is off.
trashedContacts table Off -1 year Permanently deletes contacts that have been in the trash since before the retention. Off by default: this is real data rather than housekeeping debris, and the deletion cannot be undone. Contacts CiviCRM refuses to delete permanently - those with a live contribution, those a membership type belongs to, and those linked to a site user - are always left alone. At most 500 contacts per run, so a large trash is worked through over successive days. There is no "trashed on" column, so age comes from modified_date: a contact edited after being trashed counts as trashed then.

Ported from core's Job.cleanup

Enabling this job replaces the core Clean-up Temporary Data and Files job, so every option of that job is available here. These delegate to the same core methods.

Task Type Default Core option What it does
sessions cache On session Expired sessions.
tempTables cache On tempTables Temporary tables older than 2 days.
prevNextCache cache On prevNext The search (prevnext) cache.
expiredDbCache cache On expiredDbCache Expired database cache entries.
jobLog table On jobLog Scheduled job log, keeping the newest 1000 rows plus the last 30 days.
tempFiles file On tempFiles Empties the upload directory. There is no age check, so an import file a user has uploaded but not yet processed is discarded too.
tplCache file Off tplCache Empties templates_c. Every compiled template is rebuilt on the next page load.
dbCache cache Off dbCache Rebuilds database caches. A flush rather than a purge.
memCache cache Off memCache Rebuilds the system cache.
wordReplacements cache Off wordRplc Rebuilds the word replacement cache.

Rows are deleted in batches of 5000 so a large table cannot lock the database for long enough to take the site down.

Managing tasks

Administer > System Settings > Housekeeping Tasks lists every task with its current state:

Filter the list by typing part of a task's name or description, or by type, policy and whether it is enabled.

Column Meaning
Task / Name The human readable name, and the machine name to use in the tasks parameter.
Description What the task removes.
Type / Policy What is purged (cache, table, log file, file) and how (delete old, truncate, rotate, flush).
Enabled Whether the task runs. Click to change.
Retention How long this task keeps things for, as a strtotime interval in the past, eg. -3 month or -90 days. Click to change. Shows n/a for tasks with no age component.
Last Run / Removed / Bytes Reclaimed / Last Result What happened the last time the task ran.

Retention is per task, so the rotated logs can be kept for three months while the trigger SQL goes after one, without needing a second scheduled job.

A retention that resolves to a date in the future (3 month - note the missing minus sign) is rejected rather than acted on, because it would match every file. Clearing a retention switches the task off; so does unticking Enabled, which is the clearer control.

Task, Description, Type and Policy come from the task class, and are refreshed on each run, so editing one in code reaches sites where someone has already used this screen.

Tasks come from Civi\Housekeeping\Registry and their records are managed entities, so a task added to the registry appears in this list on the next flush, and a task removed from it takes its row away. Disabling the extension deletes the records - re-enabling it recreates them at their defaults, discarding any changes made here.

If an enabled task stops running - it errors every night, or the job stops firing - a system check reports it once it has missed two of the job's intervals.

Running it by hand

cv api4 HousekeepingTask.run '{"dryRun": true}'

dryRun reports what each task would remove, and lists the files the logFiles task would delete, without removing anything.

cv api4 HousekeepingTask.run '{"tasks": ["logFiles"], "retention": "-7 day"}'

tasks limits the run to the named tasks, and runs them whether or not they are enabled. retention overrides every task's configured retention for this run only.

Adding a task

Implement Civi\Housekeeping\CleanupTaskInterface (usually by extending AbstractCleanupTask) and add the class to Civi\Housekeeping\Registry. isEnabledByDefault() and getDefaultRetention() seed the task's record when it is first created; after that the record owns both.

getType(), getPolicy() and getRetention() deliberately mirror the fields proposed for a core "HouseKeeping" entity in dev/core#5347, so these tasks can become managed entity records if that lands.