DEV Community

WP Multitool
WP Multitool

Posted on • Edited on • Originally published at wpmultitool.com

WP-CLI Commands Every WordPress Developer Should Know

The case for never opening wp-admin again

I manage around 30 WordPress sites. If I had to log into each one, click through the admin panel, run updates, check database health, clean up transients - I'd lose an entire day every week. Instead, I open a terminal and handle all of it in minutes.

That's WP-CLI. The official command-line interface for WordPress. It does everything wp-admin does, but faster, scriptable, and without a browser.

But here's the thing most WP-CLI tutorials miss: they list commands like a glossary. "Here's wp plugin list. Here's wp user create." Great - you could get that from wp help. What they don't show is how these commands fit together in real workflows. How you chain them. How you use them to debug a slow site at 2 AM when a client calls. How you build scripts that handle maintenance across dozens of sites while you sleep.

That's what this guide covers.

Installation (30 seconds)

If you don't have WP-CLI yet:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
Enter fullscreen mode Exit fullscreen mode

Verify it works:

wp --info
Enter fullscreen mode Exit fullscreen mode

On most managed hosts (Kinsta, WP Engine, Cloudways, GridPane), WP-CLI is already installed. SSH in and type wp --version to check.

Site management commands

These are the commands you'll run most often. Updates, installs, config changes.

Updates in one line

wp core update && wp plugin update --all && wp theme update --all
Enter fullscreen mode Exit fullscreen mode

That's core, all plugins, all themes. One line. What would take 15 clicks in wp-admin takes 3 seconds.

But don't be reckless. Back up first:

wp db export ~/backups/$(date +%Y%m%d).sql && wp core update && wp plugin update --all
Enter fullscreen mode Exit fullscreen mode

If something breaks, you've got the SQL dump from 10 seconds ago.

Core integrity check

wp core verify-checksums
Enter fullscreen mode Exit fullscreen mode

This compares every core file against the official WordPress.org checksums. If anything's been modified - whether by a hack, a bad deploy, or a careless edit - it tells you exactly which files. I run this on every new client site before touching anything else.

Config management

# Read a value from wp-config.php
wp config get DB_HOST

# Set a constant
wp config set WP_DEBUG true --raw

# Add a new constant
wp config set DISABLE_WP_CRON true --raw
Enter fullscreen mode Exit fullscreen mode

The --raw flag is important. Without it, WP-CLI wraps the value in quotes, turning true into the string 'true' instead of the boolean true. That's a bug you don't want to debug at midnight.

Terminal showing wp core verify-checksums success and wp config list with key configuration values

Plugin and theme management

Diagnosing plugin conflicts

Site broken and you don't know which plugin did it? This is the fastest diagnostic:

wp plugin deactivate --all
Enter fullscreen mode Exit fullscreen mode

Problem gone? Good. Now reactivate one by one:

wp plugin activate plugin-name
Enter fullscreen mode Exit fullscreen mode

Keep going until the problem returns. That's your culprit. The whole process takes 2 minutes from the command line. In wp-admin, you're clicking through confirmation dialogs for 10 minutes, and if the admin panel itself is broken, you can't even get there.

Detailed plugin info

wp plugin list --fields=name,status,version,update,auto_update
Enter fullscreen mode Exit fullscreen mode

This shows you everything at a glance - which plugins are active, which have updates pending, and which have auto-update enabled. I check this before every deployment.

Install and activate in one shot

wp plugin install query-monitor --activate
Enter fullscreen mode Exit fullscreen mode

For a fresh site setup, I'll chain these:

wp plugin install query-monitor wordfence wp-multitool --activate
Enter fullscreen mode Exit fullscreen mode

Theme operations

# Install and activate a theme
wp theme install flavor --activate

# List installed themes
wp theme list --fields=name,status,version,update

# Delete inactive themes (keep your active + one default)
wp theme delete flavor flavor-child
Enter fullscreen mode Exit fullscreen mode

Database operations

This is where WP-CLI really earns its keep. Direct database access without phpMyAdmin, without exposing port 3306, without any GUI.

Raw queries

wp db query "SELECT COUNT(*) as total_posts FROM wp_posts WHERE post_status='publish';"
Enter fullscreen mode Exit fullscreen mode

Simple. Direct. No middleman.

The autoload query I run on every site

wp db query "SELECT SUM(LENGTH(option_value)) as autoload_bytes FROM wp_options WHERE autoload='yes';"
Enter fullscreen mode Exit fullscreen mode

If that number is over 800KB, you've got a performance problem. Every single page load - every request - WordPress loads all autoloaded options into memory. A bloated wp_options table is one of the most common reasons WordPress sites slow down over time, and most developers never think to check it.

Find the worst offenders:

wp db query "SELECT option_name, LENGTH(option_value) as size_bytes FROM wp_options WHERE autoload='yes' ORDER BY size_bytes DESC LIMIT 20;"
Enter fullscreen mode Exit fullscreen mode

Terminal showing autoload query results with option sizes - elementor at 1.2MB leads the list

I wrote a full deep dive on why autoload bloat kills performance - it's one of the most overlooked issues in WordPress. The short version: plugins dump serialized data into wp_options with autoload='yes', never clean it up, and your site gets slower by the month.

To fix a specific option:

wp option autoload set bloated_option_name off
Enter fullscreen mode Exit fullscreen mode

That stops it from loading on every page. The option still exists - it just gets fetched on demand instead of preloaded. In most cases, the plugin that created it handles this gracefully.

Database maintenance

# Check all tables for errors
wp db check

# Optimize tables (reclaim space, defragment)
wp db optimize

# Get table sizes
wp db size --tables --human-readable
Enter fullscreen mode Exit fullscreen mode

The wp db size command is gold for spotting runaway tables. I've seen wp_options tables at 200MB, wp_postmeta at 500MB. Once you see which tables are bloated, you know where to dig.

Cleaning post revisions

WordPress stores every revision of every post by default. On a site with 500 posts and 5 years of history, that's easily 10,000+ revision rows:

# Count revisions
wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_type='revision';"

# Delete them all (careful - no undo)
wp post delete $(wp post list --post_type=revision --format=ids) --force
Enter fullscreen mode Exit fullscreen mode

Or limit revisions going forward:

wp config set WP_POST_REVISIONS 5 --raw
Enter fullscreen mode Exit fullscreen mode

That caps it at 5 revisions per post. Enough to recover from mistakes, not enough to bloat your database.

Search-replace: the migration command

If you've ever moved a WordPress site from one domain to another by running a raw SQL REPLACE(), you've probably corrupted serialized data. WordPress stores plugin settings, widget configs, and theme options as PHP serialized strings. Change the string length without updating the byte count, and the data becomes unreadable.

wp search-replace handles this correctly. It unserializes the data, makes the replacement, then re-serializes it with the correct byte count.

Basic domain migration

# Always dry-run first
wp search-replace 'http://staging.example.com' 'https://example.com' --all-tables --dry-run
Enter fullscreen mode Exit fullscreen mode

The dry-run output tells you exactly how many replacements would happen in each table. Review it. Make sure nothing looks unexpected.

# Then run it for real
wp search-replace 'http://staging.example.com' 'https://example.com' --all-tables
Enter fullscreen mode Exit fullscreen mode

HTTPS migration

wp search-replace 'http://example.com' 'https://example.com' --all-tables
Enter fullscreen mode Exit fullscreen mode

Staging to production workflow

This is the full workflow I use for every migration:

# 1. Export production DB as backup
wp @prod db export ~/backups/prod-$(date +%Y%m%d).sql

# 2. Export staging DB
wp @staging db export /tmp/staging.sql

# 3. Import staging DB into production
wp @prod db import /tmp/staging.sql

# 4. Search-replace URLs (dry-run first)
wp @prod search-replace 'https://staging.example.com' 'https://example.com' --all-tables --dry-run

# 5. Execute
wp @prod search-replace 'https://staging.example.com' 'https://example.com' --all-tables

# 6. Flush caches
wp @prod cache flush
wp @prod rewrite flush
Enter fullscreen mode Exit fullscreen mode

The @prod and @staging are WP-CLI aliases - I'll cover those in the advanced section.

Terminal showing wp search-replace dry-run results with replacements per database table

User management

Emergency admin access

This is the command I use most when a site goes down and the admin panel is inaccessible. SSH in, create a temporary admin:

wp user create tempadmin admin@example.com --role=administrator --user_pass=$(openssl rand -base64 12)
Enter fullscreen mode Exit fullscreen mode

Fix the problem. Then clean up:

wp user delete tempadmin --reassign=1
Enter fullscreen mode Exit fullscreen mode

The --reassign=1 flag transfers any content owned by that user to user ID 1 (usually the main admin). Don't skip this - deleting a user without reassigning can orphan their posts.

Audit admin accounts

wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
Enter fullscreen mode Exit fullscreen mode

I run this on every client site during onboarding. You'd be surprised how often there's a "developer" account from 3 years ago that nobody remembers creating. Every unused admin account is a security risk.

Bulk password resets

After a breach (or suspected breach):

wp user list --role=administrator --field=user_login | while read user; do
  newpass=$(openssl rand -base64 16)
  wp user update "$user" --user_pass="$newpass"
  echo "$user: $newpass"
done
Enter fullscreen mode Exit fullscreen mode

Save the output, distribute new passwords securely, done. This takes 10 seconds for 5 admins. Good luck doing that through wp-admin during an active incident.

Cron management

WordPress cron is not real cron. It's "pseudo-cron" - it only runs when someone visits the site. Low-traffic site? Your scheduled tasks might run hours late. Or never.

See what's scheduled

wp cron event list
Enter fullscreen mode Exit fullscreen mode

This shows every scheduled event, when it's due, and how often it recurs. On most sites, you'll see 20-40 events. Some of them are legitimate (checking for updates, sending scheduled posts). Some are leftovers from plugins you deleted years ago.

Run overdue events

wp cron event run --due-now
Enter fullscreen mode Exit fullscreen mode

Test a specific hook

wp cron event run wp_scheduled_delete
Enter fullscreen mode Exit fullscreen mode

Switch to real cron

For production sites, disable WordPress's pseudo-cron and use system cron instead:

wp config set DISABLE_WP_CRON true --raw
Enter fullscreen mode Exit fullscreen mode

Then add a system crontab entry:

*/5 * * * * cd /var/www/yoursite && wp cron event run --due-now --quiet 2>&1
Enter fullscreen mode Exit fullscreen mode

This runs WP-CLI cron every 5 minutes regardless of traffic. Reliable. Predictable. No more missed scheduled posts.

Find orphaned cron events

After deactivating a plugin, its cron events often stick around:

wp cron event list --fields=hook,next_run_relative,recurrence | grep -v "WordPress"
Enter fullscreen mode Exit fullscreen mode

If you see hooks from plugins that aren't active anymore, clean them up:

wp cron event delete old_plugin_sync_hook
Enter fullscreen mode Exit fullscreen mode

Transient management

Transients are WordPress's caching mechanism for temporary data. API responses, query results, feed data. They expire, but WordPress is lazy about cleaning them up.

# Delete only expired transients
wp transient delete --expired

# Delete ALL transients (safe - they regenerate on demand)
wp transient delete --all

# Count total transients
wp transient list --format=count
Enter fullscreen mode Exit fullscreen mode

On WooCommerce sites and sites with heavy API integrations, I've seen 50,000+ expired transient rows sitting in wp_options. That's dead weight in your database, and since many transients are autoloaded, they're dead weight in memory too.

# Check transient overhead specifically
wp db query "SELECT COUNT(*) as transient_count, SUM(LENGTH(option_value)) as total_bytes FROM wp_options WHERE option_name LIKE '%_transient_%';"
Enter fullscreen mode Exit fullscreen mode

Performance debugging from the command line

This is where WP-CLI becomes a diagnostic tool, not just a management tool.

Memory and load time baseline

wp eval 'echo "Memory: " . round(memory_get_peak_usage()/1024/1024, 2) . " MB\n";'
Enter fullscreen mode Exit fullscreen mode

Object cache status

wp cache type
Enter fullscreen mode Exit fullscreen mode

If this returns "Default", you're not using an object cache. That means every page load hits the database for things Redis or Memcached could serve from memory.

List autoloaded options sorted by size

wp db query "SELECT option_name, LENGTH(option_value) as size_bytes FROM wp_options WHERE autoload = 'yes' ORDER BY size_bytes DESC LIMIT 30;"
Enter fullscreen mode Exit fullscreen mode

WP-CLI's wp option list doesn't sort by size natively, so I use the raw SQL query. I run this more than almost any other command when diagnosing slow queries and general performance issues.

Slow plugin detection

Want to know which plugins are slowing down your site? Query Monitor helps in the browser, but from the CLI:

# Time how long WP takes to bootstrap
time wp eval "echo 'loaded';"

# Now deactivate a suspect plugin and time again
wp plugin deactivate heavy-plugin
time wp eval "echo 'loaded';"
Enter fullscreen mode Exit fullscreen mode

If the load time drops significantly, you found your problem. Reactivate and decide what to do about it.

Terminal comparing wp eval load times before and after deactivating a heavy plugin - 66% faster

Export options for comparison

# Save current autoloaded options to a file
wp option list --autoload=yes --format=csv > options-before.csv

# After making changes, export again
wp option list --autoload=yes --format=csv > options-after.csv

# Diff them
diff options-before.csv options-after.csv
Enter fullscreen mode Exit fullscreen mode

This is how I track what plugins do to wp_options during activation. Some plugins add 500KB of autoloaded data the moment you activate them. Good to know before you commit to using them in production.

Advanced: aliases, scripts, and automation

WP-CLI aliases

Aliases let you run commands on remote sites from your local machine. Define them in ~/.wp-cli/config.yml:

@prod:
  ssh: deploy@production.example.com/var/www/html
@staging:
  ssh: deploy@staging.example.com/var/www/html
@dev:
  path: /home/user/sites/dev
Enter fullscreen mode Exit fullscreen mode

Now you can run:

wp @prod plugin list
wp @staging db export /tmp/staging-backup.sql
wp @prod core version
Enter fullscreen mode Exit fullscreen mode

No need to SSH in manually, navigate to the site directory, and run the command. One line from your laptop.

Alias groups

@all:
  - @prod
  - @staging
  - @dev
Enter fullscreen mode Exit fullscreen mode
wp @all core version
Enter fullscreen mode Exit fullscreen mode

Checks the WordPress version on all three environments in one command.

Bash aliases for common tasks

Add these to your ~/.bashrc or ~/.zshrc:

# Quick site health check
alias wphealth='wp core verify-checksums && wp plugin list --update=available --format=count && echo "plugins need updates"'

# Backup and update
alias wpupdate='wp db export ~/backups/pre-update-$(date +%Y%m%d-%H%M).sql && wp plugin update --all && wp core update'

# Autoload size check
alias wpautoload='wp db query "SELECT ROUND(SUM(LENGTH(option_value))/1024) as KB FROM wp_options WHERE autoload=\"yes\";"'

# Quick cleanup
alias wpclean='wp transient delete --expired && wp cache flush'
Enter fullscreen mode Exit fullscreen mode

Multi-site maintenance script

Here's the script I actually use for weekly maintenance across all sites:

#!/bin/bash
SITES=("@client1" "@client2" "@client3" "@mysite")
BACKUP_DIR="$HOME/backups/$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"

for site in "${SITES[@]}"; do
  echo "=== Processing $site ==="

  # Backup (exports via SSH, pipes to local file)
  wp "$site" db export - --quiet > "$BACKUP_DIR/${site#@}.sql"

  # Updates
  wp "$site" plugin update --all --quiet
  wp "$site" core update --minor --quiet

  # Cleanup
  wp "$site" transient delete --expired --quiet

  # Health check
  wp "$site" core verify-checksums 2>&1 | grep -v "Success"

  # Report autoload size
  echo -n "  Autoload: "
  wp "$site" db query "SELECT ROUND(SUM(LENGTH(option_value))/1024) as KB FROM wp_options WHERE autoload='yes';" --skip-column-names

  echo ""
done

echo "Done: $(date)"
Enter fullscreen mode Exit fullscreen mode

I run this every Monday morning. 30 sites, 5 minutes, zero clicks.

Chaining commands with conditionals

# Only update if there are updates available
wp plugin list --update=available --format=count | grep -q "^0$" || wp plugin update --all

# Verify checksums and alert on failure
wp core verify-checksums || echo "CHECKSUM FAILURE on $(wp option get siteurl)" | mail -s "WP Alert" admin@example.com
Enter fullscreen mode Exit fullscreen mode

Real-world workflow: staging to production

Here's the complete workflow I follow for deploying changes from staging to production. Not the simplified version - the actual steps:

# 1. Snapshot production (safety net)
wp @prod db export ~/backups/prod-pre-deploy-$(date +%Y%m%d-%H%M).sql

# 2. Put production in maintenance mode
wp @prod maintenance-mode activate

# 3. Export staging database
wp @staging db export /tmp/staging-deploy.sql

# 4. Import into production
wp @prod db import /tmp/staging-deploy.sql

# 5. Search-replace URLs
wp @prod search-replace 'https://staging.example.com' 'https://example.com' --all-tables --precise

# 6. Sync uploads (rsync, not WP-CLI)
rsync -avz --delete staging.example.com:/var/www/html/wp-content/uploads/ /var/www/html/wp-content/uploads/

# 7. Flush everything
wp @prod cache flush
wp @prod rewrite flush
wp @prod transient delete --all

# 8. Verify
wp @prod option get siteurl
wp @prod core verify-checksums

# 9. Disable maintenance mode
wp @prod maintenance-mode deactivate
Enter fullscreen mode Exit fullscreen mode

The --precise flag on search-replace forces PHP-based replacement for all columns, not just the ones WP-CLI detects as serialized. Slower, but catches edge cases.

Custom WP-CLI commands

WP-CLI is extensible. You can register your own commands that run alongside the built-in ones. This is useful when you have project-specific tasks that you repeat across sites.

The basics:

// In a plugin or mu-plugin file
if (defined('WP_CLI') && WP_CLI) {
    WP_CLI::add_command('mycommand', function($args, $assoc_args) {
        $count = $assoc_args['count'] ?? 10;

        $posts = get_posts([
            'numberposts'  => $count,
            'post_type'    => 'post',
            'post_status'  => 'draft',
        ]);

        if (empty($posts)) {
            WP_CLI::success('No orphaned drafts found.');
            return;
        }

        foreach ($posts as $post) {
            WP_CLI::line("{$post->ID}: {$post->post_title} (created: {$post->post_date})");
        }

        WP_CLI::warning(count($posts) . ' orphaned drafts found.');
    });
}
Enter fullscreen mode Exit fullscreen mode
wp mycommand --count=20
Enter fullscreen mode Exit fullscreen mode

For anything more complex, use a class-based approach with proper docblock annotations for argument parsing and wp help integration. The WP-CLI Commands Cookbook covers this in detail.

I use custom commands for things like:

  • Auditing user roles and capabilities across multisite
  • Generating reports on plugin usage patterns
  • Running project-specific data migrations that need WordPress context

WP Multitool takes this concept further with 7 built-in subcommands designed for performance auditing:

# Full site health check
wp multitool health

# Database health report (table sizes, overhead, fragmentation)
wp multitool db-health

# Autoload analysis with actionable recommendations
wp multitool autoload --report

# Capture and analyze slow queries
wp multitool slow-queries --threshold=100
Enter fullscreen mode Exit fullscreen mode

The slow query analysis runs EXPLAIN on each query automatically and tells you exactly which indexes to add. That's something I wished existed in core WP-CLI for years before I built it.

WP Multitool health report showing database health, query performance and overall site score of 82

Quick reference

Here's every command from this guide in one place, organized by what you're trying to do:

Task Command
Update everything wp core update && wp plugin update --all && wp theme update --all
Backup database wp db export backup.sql
Verify core files wp core verify-checksums
Check autoload size wp db query "SELECT SUM(LENGTH(option_value)) FROM wp_options WHERE autoload='yes';"
Find biggest options wp db query "SELECT option_name, LENGTH(option_value) as bytes FROM wp_options WHERE autoload='yes' ORDER BY bytes DESC LIMIT 20;"
Migrate domains wp search-replace 'old.com' 'new.com' --all-tables
Create temp admin wp user create temp temp@x.com --role=administrator
Deactivate all plugins wp plugin deactivate --all
Delete expired transients wp transient delete --expired
Run overdue cron wp cron event run --due-now
Check object cache wp cache type
Database table sizes wp db size --tables --human-readable
Optimize database wp db optimize

What's next

If you got this far, you don't need more WP-CLI tutorials. You need to start using it daily until the commands become muscle memory. Set up aliases for your sites. Write one maintenance script. Run it next Monday.

If you're curious what your site looks like under the hood before diving in, run a free scan and see what comes up. Autoload bloat, slow queries, missing indexes - the report tells you exactly which commands to run.

Top comments (0)