This project is not covered by Drupal’s security advisory policy.
Drupal Cleanup finds and safely removes the data a Drupal site leaves behind: search index entries, moderation states, queue items and custom-table rows for content that no longer exists, abandoned batch jobs, and old working files. Every removal is backed up first, can be previewed with a dry run, and can be undone.
Core cleans up after itself when content is deleted normally. Real sites also lose content in ways that skip the entity API: direct SQL, migration rollbacks, bulk deletes that die part-way, or code from before core handled it. What gets left behind is never cleaned up. It bloats tables, slows queries and backups, and can surface dead results. Drupal Cleanup finds it and removes it on a schedule, and never deletes anything on guesswork.
Built for government and regulated sites
Government sites can't just delete leftover data: records schedules, legal holds, public-records requests and audit requirements decide what may be destroyed, and when. Drupal Cleanup is designed for that:
- Hold switch: one setting, or
drush cleanup:hold on --reason="...", stops every deletion (scheduled, manual and after content deletes) and blocks backup deletion, for legal holds and public-records requests. Dry runs and restores keep working. - Retention periods: keep removable items for a set number of days before they are deleted, as a site default or per task. Modules can require a minimum for their own data, and the longest period always wins, so no module can make a site delete earlier than its policy allows.
- Audit trail: every removal is recorded before anything is deleted, listing the exact items and who ran it when, followed by the result. It is written to a permanent audit log and to its own log channel, so it can reach syslog and central log collection. No record, no delete.
- Reversible by design: a backup of exactly what is removed, restorable with one command, stored only outside the web root.
- Nothing automatic unless you want it: every task can be turned off, and scheduled runs and cleanup after deletes can be disabled, leaving removal to an administrator.
Which data counts as a record, and for how long, is your records officer's decision. Drupal Cleanup gives them the controls. See Compliance and records retention in the documentation.
What it cleans
- Search index orphans: Core Search entries for content that no longer exists, cleared through core's own
search.indexservice rather than raw SQL. - Content moderation orphans: moderation states, with all their revisions, whose content is gone, for every moderated entity type.
- Abandoned batch jobs: old rows in the
batchtable and their leftoverdrupal_batch:*queues. Core has a cleanup method for this, but nothing calls it, so the table only ever grows. - Custom table orphans: any module can declare its own tables of entity IDs in a small YAML file. No PHP needed, and no dependency: without Drupal Cleanup, the file is ignored.
- Queue items for deleted content: unprocessed jobs whose target is gone, before a worker trips over them. Claimed items are never touched.
- Unused modules and themes report: lists enabled modules and installed themes that look unused, development modules left on, and code on disk that isn't installed at all, with the evidence for each. Less code means less to patch, scan and accredit. Report only: nothing is uninstalled.
- Large files report: the biggest files on the site, what uses each one (including links in body text and the theme logo or favicon), and copies stored twice, with an estimate of what could be saved. Report only.
- Code and cache mismatch check: finds cached hook implementations that the deployed code no longer has (the cause of site-wide errors when two copies of a site share a database, or files are rolled back without a cache rebuild). One Drush option rebuilds the caches from the right code.
- Old working files: scratch folders that imports and conversions leave on disk, removed by age and only from folders a module has declared. They are moved to a trash folder next to the backup, not deleted outright.
Safe by design
- Backed up before every delete, no exceptions. If the backup can't be written, nothing is deleted. Backups are listed on their own admin tab, with their size, and
drush cleanup:restoreputs everything back. - Only provable orphans. A row is removed only when the entity it points at really doesn't exist. Nothing is ever deleted by ID range or date.
- Dry run everywhere. See exactly what each task would remove, and what is still waiting out its retention period, before anything changes.
- Bounded and isolated. Each task works in limited chunks per run; a misconfigured or failing task reports why and never stops the others.
- Cleanup right after deletes. Declared rows are removed at the end of the request that deleted their content, in one batched, backed-up run, so even bulk deletes stay fast. A failure there never blocks the delete.
Getting started
- Make sure the site has a private file system outside the web root (
$settings['file_private_path']), or set a backup location outside it. Without one, Drupal Cleanup refuses to delete anything. - Go to Configuration » System » Cleanup (
/admin/config/system/cleanup). Choose the tasks, how often cron runs the sweep (default every 6 hours), retention periods, and how long backups are kept. - Look before anything is removed:
drush cleanup:list drush cleanup:run --dry-run - Run it with
drush cleanup:run, or leave it to cron. Manage backups on the Backups tab or withdrush cleanup:backups; review removals withdrush cleanup:audit.
For module developers
Declare your module's tables, queues and scratch folders in MODULE.drupal_cleanup.yml:
mymodule_log:
label: 'My module log rows for deleted content'
table: mymodule_log
key: id
references:
- { column: nid, entity_type: node }
retention_days: 365
queues:
mymodule_process:
entity_type: node
data: scalar
files:
mymodule_tmp:
paths: ['public://mymodule/tmp']
max_age: 604800Your declarations live in your module, so updating Drupal Cleanup never touches them. Every cleanup is a CleanupTask plugin, so modules can also ship their own tasks.
Requirements
Drupal 10.3 or later, or Drupal 11. No other modules or libraries. Drush 12.5 or later for the command-line tools (optional). The search and moderation tasks appear automatically when core's Search and Content Moderation modules are enabled.
Documentation
Full documentation, with a page for each task plus backups and compliance: Drupal Cleanup documentation.
Supporting this module
Bug reports, feature requests and patches are welcome in the issue queue. If your module keeps entity IDs in its own tables or queues, a MODULE.drupal_cleanup.yml file is a small, dependency-free way to keep the sites that use it clean.
Originally built for DocSyntra by EasyLifeAgents via Claude Code.
More Modules
- Block List: Lists every block on the site by the module that provides it, core included, with a plain-English description of each block and every place it is used.
- Config Doctor: Finds configuration nothing uses and dependency problems that break imports, and lets configuration imports fix themselves instead of failing.
- Module Cleanup: Lists uninstalled or deleted modules with leftover transient data and allows the admin to delete any leftover data. A fix for the "Module 'module_name' has an entry in the system.schema key/value storage" error.
- NIST for Gov: Reports that check a Drupal site against NIST SP 800-53 Rev. 5 controls, starting with API keys, passwords and tokens stored in plain text.
- Permissions Table: Fixes the permissions table by keeping its header fixed at the top and its horizontal scrollbar fixed at the bottom.
- Recipe Filter: Adds a "Recipe Type" sidebar menu to the Project Browser Recipes page, the same way Module Filter builds a sidebar for the Modules page.
- Sentinel: Warns people about danger where they are: FEMA public alerts, National Weather Service alerts and FEMA flood zones, with a warning when a flood alert hits a high-risk flood zone.
- Uninstall Impact: Shows what uninstalling a module deletes, before you do it.
Related sites
Project information
- Project categories: Developer tools
- Created by trigve hagen on , updated
This project is not covered by the security advisory policy.
Use at your own risk! It may have publicly disclosed vulnerabilities.
