---
title: "WordPress Database Maintenance: What Grows, What To Prune, What To Leave Alone"
url: https://hostmy.blog/wordpress-database-maintenance/
date: 2026-09-12
modified: 2026-09-03
lang: en
author: "Aditya Sharma"
description: "A WordPress database grows in predictable places. Here is how to find them, what is safe to delete, and which cleanups make things worse."
categories:
  - "WordPress"
image: https://hostmy.blog/wp-content/uploads/2026/09/hmb-card-1857-1024x538.jpg
word_count: 1672
---

# WordPress Database Maintenance: What Grows, What To Prune, What To Leave Alone

A WordPress database grows in a small number of predictable places. Knowing which ones lets you skip the one-click "optimise" button, which is where most database damage comes from.

The honest framing first. Database cleanup rarely fixes a slow site, and none of it appears in [the five changes that did move the number for us](/wordpress-speed-optimization/). There are two exceptions that matter a great deal, autoloaded option bloat and runaway log tables, and outside those two the speed benefit of deleting rows is usually close to zero. Cleanup is mostly about keeping backups small, restores fast, and future problems visible.

## Start by measuring, because the growth is never where people assume

`wp db size --tables --human-readable`

That one command sorts the problem. Read it top down and note anything that surprises you, especially tables belonging to plugins rather than core. It is one of the weekly lines in [a maintenance month itemised task by task](/wordpress-maintenance-service/).

Then the number that matters more than total size:

`wp db query "SELECT COUNT(*) AS rows_count, \
ROUND(SUM(LENGTH(option_value))/1024/1024,2) AS mb \
FROM $(wp db prefix)options WHERE autoload IN ('yes','on','auto-on','auto');"`

The four values in that `IN` list are not decoration. WordPress 6.6 changed the `autoload` column from plain `yes` and `no` to `on`, `off`, `auto`, `auto-on` and `auto-off`, and core now treats `yes`, `on`, `auto-on` and `auto` as autoloaded. The older one line version of this query, `WHERE autoload='yes'`, returns NULL on any site running 6.6 or later, which reads exactly like a site with no autoload problem at all.

Autoloaded options load into memory on every single request, front end and admin, whether or not the page uses them. A 500MB database with a small autoload set is healthier than a 60MB database with a 20MB autoload set. The first is storage, the second is a tax on every page view, which is why it appears in [the Core Web Vitals sequence](/core-web-vitals-wordpress/) as well.

Find the individual offenders:

`wp db query "SELECT option_name, ROUND(LENGTH(option_value)/1024,1) AS kb \
FROM $(wp db prefix)options WHERE autoload IN ('yes','on','auto-on','auto') \
ORDER BY LENGTH(option_value) DESC LIMIT 20;"`

## The six places WordPress databases grow

| Table | What accumulates | Typical cause |
| ----- | ---------------- | ------------- |
| `options` | Transients, cached payloads, plugin leftovers | Plugins caching into options with autoload on |
| `postmeta` | Builder layout data, orphaned rows, lock and edit meta | Page builders, plugins removed without cleanup |
| `posts` | Revisions, auto-drafts, trashed items, orphan attachments | Default unlimited revisions |
| `comments` and `commentmeta` | Spam and trash awaiting a purge nobody schedules | Open comments without cleanup |
| `usermeta` | Session tokens, per-user plugin state | Membership and LMS plugins, large user bases |
| Plugin tables | Logs, form entries, 404 records, queued actions | Anything that writes a row per request |

Plugin tables are the ones to look at first on an older site. Security event logs, redirect miss logs, analytics tables and job queues all write on a per-request basis, and a bot crawl can add tens of thousands of rows in an afternoon. Those tables can end up larger than all of core combined.

Action Scheduler, used by WooCommerce and many other plugins, is a specific case worth knowing. It keeps completed and failed actions, and on a busy store the `actionscheduler_actions` and `actionscheduler_logs` tables can dominate the database. It has its own retention setting, and raising the cleanup frequency is a better fix than deleting rows by hand.

The six places a WordPress database grows

The six places a WordPress database grows

options
transients and cached payloads, autoloaded

postmeta
builder layout data, orphaned rows, edit locks

posts
revisions, auto-drafts, trashed items

comments and commentmeta
spam and trash awaiting a purge nobody scheduled

usermeta
session tokens, per-user plugin state

Plugin tables
logs, form entries, 404 records, queued actions

Plugin tables are the ones to look at first on an older site, because anything writing a row per request outgrows the tables WordPress ships with.

## Safe to prune, in rough order of safety

**Expired transients.** These are cached values with an expiry that has already passed, and WordPress does not always clean them up promptly.

`wp transient delete --expired`

**Spam and trashed comments.** Nothing here is content you want.

`wp comment delete $(wp comment list --status=spam --format=ids) --force
wp comment delete $(wp comment list --status=trash --format=ids) --force`

**Old revisions, keeping a floor.** Revisions are a real feature and deleting all of them is a choice, not a cleanup. Cap them going forward first:

`wp config set WP_POST_REVISIONS 5 --raw`

That limit applies to new saves. For the existing backlog, delete in batches and check the count first so you know what you are about to remove:

`wp post list --post_type=revision --format=count`

**Auto-drafts that were never used.** These are created every time someone opens the new-post screen and abandons it.

`wp post list --post_type=any --post_status=auto-draft --format=count`

**Plugin logs with a retention setting.** Configure the retention rather than truncating the table, so it stays fixed.

## Prune with care, after a backup

**Orphaned postmeta.** Rows whose parent post no longer exists. Usually genuine debris from deleted content and removed plugins.

`SELECT COUNT(*) FROM wp_postmeta pm
LEFT JOIN wp_posts p ON p.ID = pm.post_id
WHERE p.ID IS NULL;`

Count it before you delete it, and take a database dump first. Some plugins deliberately store rows against a post ID that does not correspond to a real post, and a blanket delete takes those with it. If the count is a few hundred, the cleanup is not worth the risk. If it is in the millions, it is worth doing carefully.

**Autoload flags on large options.** Switching a large option from autoload yes to no is often the single highest-value database change available. It is also the one that needs attribution: identify which plugin owns the option before touching it, because a plugin that expects its value to be preloaded can behave oddly when it is not.

`UPDATE wp_options SET autoload = 'off' WHERE option_name = 'the_specific_option';`

One option at a time, with a check of the site afterwards, and ideally on [a staging copy that still matches production](/wordpress-staging/) before it happens anywhere real.

## Leave these alone

**Options you cannot attribute.** An unexplained row is a reason to investigate, not to delete. On an old site the mystery option is occasionally holding a license key, a redirect map, or a migration flag.

**Postmeta belonging to an active page builder.** Builder layouts live in postmeta, they are large by nature, and deleting them destroys page content with no undo short of a restore.

**Order, subscription and customer tables.** These are business records, frequently with legal retention obligations. Size is not a reason to touch them.

**Indexes.** Some cleanup tools offer to drop or rebuild indexes. A missing index turns a fast query into a table scan, and the effect grows with the table.

**`OPTIMIZE TABLE` on InnoDB, run casually.** On InnoDB, which is the default storage engine for modern WordPress, the command rebuilds the table rather than doing a light defragmentation. It needs free disk space roughly equal to the table size, and the benefit is usually small. On a regular InnoDB table it runs as online DDL and allows concurrent reads and writes, taking a brief exclusive lock only at the start and the end, but a table with a FULLTEXT index falls back to the locking copy method. It is a deliberate maintenance action for a table that has had a large deletion, not a weekly habit, and it is a bad idea on a live shop mid-day.

## Prevention beats cleanup

Four settings that stop the growth instead of chasing it:

`// wp-config.php
define( 'WP_POST_REVISIONS', 5 ); // cap revisions per post
define( 'AUTOSAVE_INTERVAL', 120 ); // fewer autosave writes
define( 'EMPTY_TRASH_DAYS', 14 ); // purge trash automatically`

Plus one habit: when you remove a plugin, remove its data deliberately. Uninstalling through the admin runs the plugin's own cleanup routine, while deleting the folder over SFTP leaves every table and option behind forever. That distinction is part of the removal sequence in [the six places a plugin costs you](/wordpress-plugin-bloat/).

Content is the one category this work should not decide. Whether an old post is updated, merged or deleted is an editorial call rather than a storage one, and [spotting the posts that have gone stale](/find-stale-posts/) is where that starts.

## The maintenance cadence that is enough

| Frequency | Action |
| --------- | ------ |
| Weekly | Look at `wp db size --tables`, note anything growing fast |
| Monthly | Expired transients, spam and trash purge, check autoload total |
| Quarterly | Review plugin log tables and their retention settings |
| After any plugin removal | Check for orphan tables and options left behind |
| Before any of it | Take a backup you can actually restore |

That last row is not decoration. Every operation above is a delete, deletes have no undo, and a backup you have never restored is a guess. The reasoning is in [what we changed after a backup did not come back](/wordpress-backup/), and the five minutes it takes to dump the database first is the cheapest insurance in this article:

`wp db export pre-cleanup-$(date +%F).sql`

The same question is worth asking before you install anything new: what does it write, and where. One layer we do keep on client blogs is AI crawler handling, using [RankReady](https://wordpress.org/plugins/rankready-ai-llm-seo/), a WordPress AI SEO plugin at version 1.3.1, tested to WordPress 7.1, with about 300 active installs on WordPress.org. It runs alongside Rank Math, Yoast, AIOSEO or SEOPress without conflicting with them and takes about five minutes to configure. Audit its options rows the way you would audit any other plugin's, and judge it on crawlability rather than on anything promised about rankings.

## Where Host My Blog fits

The commands above are the job. Nothing here needs a service, and a careful owner can run the monthly cadence in fifteen minutes.

Host My Blog does it as scheduled work on the blogs we look after, mainly because the failure mode is neglect rather than difficulty. Databases grow quietly for years and then show up as a slow admin, a backup that times out, or a restore that takes hours when you have minutes. If you will genuinely run the cadence yourself, run it and keep the money.

Worth checking before you close this tab: what is your total autoloaded size right now, and can you name the plugin behind the largest row?