How to use this reference
This article covers the WP-CLI commands I reach for on a daily or weekly basis when managing WordPress sites. It is not the full command list (that lives in the official command reference and covers 46+ top-level commands). It is the subset that handles 90% of real operational work: updates, plugin management, user operations, database exports, URL replacements, cron, cache flushes, and health checks.
Version scope. All commands are verified against WP-CLI 2.12.0 (released May 7, 2025), which requires PHP 7.2.24 or newer and supports PHP 8.4. Where a flag or subcommand was added in a specific version, the version is noted inline.
Contents:
- Installing WP-CLI
- Global flags worth knowing
- Core management
- Plugin management
- Theme management
- User management
- Database operations
- Search and replace
- Configuration management
- Cron management
- Cache and transients
- Health checks with wp doctor
- Running WP-CLI in Docker
- Useful aliases and shell scripts
- Common misconceptions
Installing WP-CLI
The recommended method is the Phar archive. Download, make executable, move to a directory in your $PATH:
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
wp --info
The last command prints the WP-CLI version, PHP version, PHP binary path, and wp-config.php path (if run inside a WordPress root). If wp --info works, the install is good.
Updating is one command: wp cli update. It fetches the latest stable Phar and overwrites the existing binary.
Alternative methods: Composer (composer global require wp-cli/wp-cli-bundle), Homebrew on macOS (brew install wp-cli), and OS packages. In Docker, the official wordpress:cli image ships a ready-to-use WP-CLI binary (see Running WP-CLI in Docker).
Global flags worth knowing
Every WP-CLI command accepts these flags. They are not specific to any subcommand, and they solve problems that come up in scripting, debugging, and multisite work:
| Flag | What it does |
|---|---|
--path=<path> |
Points WP-CLI at a WordPress root other than the current directory. |
--url=<url> |
Targets a specific site in a multisite setup. Required for multisite; ignored on single-site. |
--skip-plugins[=<plugins>] |
Boots WordPress without loading all (or specific) plugins. Saves your session when a broken plugin prevents WP-CLI from running. |
--skip-themes[=<themes>] |
Same, but for themes. |
--format=<format> |
Output as table, json, csv, yaml, ids, or count. The json and ids formats are what you pipe into other commands. |
--quiet |
Suppresses informational messages. Useful in cron scripts where you only want error output. |
--debug[=<group>] |
Prints internal debug output. Invaluable when a command silently fails. |
--allow-root |
Suppresses the "running as root" security warning. See the Docker section for when this is appropriate. |
Source: WP-CLI configuration reference.
Core management
# Check if a newer WordPress version is available.
wp core check-update
# Download and install the update.
wp core update
# Run the database upgrade routine (equivalent to visiting /wp-admin/upgrade.php).
wp core update-db
# Verify that core files have not been modified.
# This is the fastest first step when you suspect a compromise.
wp core verify-checksums
wp core verify-checksums compares every core file against the checksums published by WordPress.org. Any modified, added, or missing file is reported. In WP-CLI 2.12.0, the --exclude=<files> flag was added so you can whitelist known customisations (a patched wp-config-sample.php, for instance) without false positives.
wp core download --locale=nl_NL downloads a specific locale. Useful for scripted installs where the language pack needs to be in place before the first boot.
Source: wp core commands.
Plugin management
# List all plugins, their status, and whether updates are available.
wp plugin list
# Install and activate in one step.
wp plugin install woocommerce --activate
# Update every plugin with a pending update.
wp plugin update --all
# Deactivate a plugin without deleting its files.
wp plugin deactivate akismet
# Delete a plugin (removes files; does NOT run uninstall hooks).
wp plugin delete hello
# Check whether a plugin is active (exit code 0 = active, 1 = not active).
# Useful in shell scripts.
wp plugin is-active woocommerce
As of WP-CLI 2.12.0, wp plugin update --all respects the requires and requires_php headers in plugin metadata. If your WordPress or PHP version is too low for a plugin update, the plugin is marked "unavailable" instead of silently failing. The --force-check flag forces a fresh update check against the WordPress.org API, bypassing the transient cache.
For scripting bulk updates with a report, pipe the output:
# List plugins that have updates available, output only slugs.
wp plugin list --update=available --format=ids
# Returns: akismet woocommerce yoast-seo
# Pipe into update (xargs handles the list).
wp plugin list --update=available --format=ids | xargs wp plugin update
Source: wp plugin commands.
Theme management
The pattern mirrors plugins exactly: wp theme install, wp theme activate, wp theme update --all, wp theme delete, wp theme list. The same --force-check flag from 2.12.0 applies.
# Switch to a theme, installing it first if needed.
wp theme install flavor --activate
# Update all themes.
wp theme update --all
One difference: wp theme activate accepts only one theme at a time (you can only have one active theme), whereas wp plugin activate accepts multiple slugs.
Source: wp theme commands.
User management
# Create a user with the administrator role.
# WP-CLI auto-generates a secure password and prints it to stdout.
wp user create jan jan@yoursite.nl --role=administrator
# List all users, showing ID, login, email, and role.
wp user list
# Reset a user's password (sends the password reset email).
wp user reset-password 1
# Update a password directly (avoid this in shared terminals; see note below).
wp user update 1 --user_pass='new-password-here'
# Delete a user and reassign their posts to user ID 2.
# Always use --reassign to prevent orphaned posts.
wp user delete 5 --reassign=2
Shell history warning. wp user create with --user_pass and wp user update with --user_pass write the password into your shell history. On a shared server, that is a credential leak. Use --prompt=user_pass instead, which prompts interactively:
wp user update 1 --prompt=user_pass
The same applies to wp config create --dbpass: prefer --prompt=dbpass or set credentials via environment variables.
Source: wp user commands.
Database operations
# Export the full database to a file with a date stamp.
wp db export backup-$(date +%Y%m%d).sql
# Import from a dump.
wp db import backup-20260408.sql
# Run a raw SQL query.
wp db query "SELECT option_value FROM wp_options WHERE option_name = 'siteurl'"
# Check table integrity.
wp db check
# Optimize tables (useful after mass deletes or WooCommerce order purges).
wp db optimize
# Report database size per table.
wp db size --tables
All wp db commands read credentials from wp-config.php. You never pass database usernames or passwords on the command line.
wp db reset --yes drops and recreates every table. It is the nuclear option. The --yes flag is required precisely because the command is irreversible without a backup.
Source: wp db commands.
Search and replace
wp search-replace is the most consequential single command in WP-CLI. It rewrites strings across the entire database, handles serialised PHP data correctly, and has no undo. The dedicated article on WordPress search-replace after migration covers the full workflow including the Gutenberg JSON-escaped URL trap and per-table scope. Here is the command-level reference.
# Step 1: ALWAYS dry-run first. Shows what would change without writing.
wp search-replace 'https://old.yoursite.nl' 'https://new.yoursite.nl' \
--all-tables-with-prefix \
--skip-columns=guid \
--dry-run
# Step 2: Commit the replacement.
wp search-replace 'https://old.yoursite.nl' 'https://new.yoursite.nl' \
--all-tables-with-prefix \
--skip-columns=guid \
--precise
Flags that matter
| Flag | Purpose |
|---|---|
--dry-run |
Runs the operation, reports counts, writes nothing. Non-negotiable first step. |
--precise |
Forces PHP-level processing for every row instead of SQL string replacement. Without this flag, serialised data can be silently corrupted when the replacement changes string length. |
--all-tables-with-prefix |
Includes every table that starts with the WordPress prefix, catching plugin tables (WooCommerce HPOS, Yoast, Wordfence) that $wpdb does not register. |
--skip-columns=guid |
Protects the wp_posts.guid column, which must never be changed to avoid breaking RSS feeds. |
--network |
For multisite: runs across all per-site tables. |
--report-changed-only |
Only prints tables where replacements occurred. Cleaner output on large databases. |
Source: wp search-replace.
Configuration management
# Generate a wp-config.php from scratch.
wp config create --dbname=mydb --dbuser=user --dbpass=pass --locale=en_US
# Read a constant.
wp config get WP_DEBUG
# Set a boolean constant (--raw means the value is not quoted in PHP).
wp config set WP_DEBUG true --raw
# Check whether a constant is truthy (exit code 0 = true).
# Added in WP-CLI 2.9.0. Useful in shell conditionals.
wp config is-true WP_DEBUG
# List all defined constants, variables, and file includes.
wp config list
For a deeper dive into what each constant does and which defaults matter, the wp-config.php reference covers the full set.
Source: wp config commands.
Cron management
WP-Cron is a pseudo-scheduler that fires only when someone visits the site. On low-traffic sites or sites behind aggressive page caching, scheduled events silently stop running. The reliable fix is to disable WP-Cron and replace it with a system cron job.
# List all scheduled cron events with their next run time.
wp cron event list
# Manually trigger a specific hook.
wp cron event run wp_update_plugins
# Run all events that are currently due.
wp cron event run --due-now
# List defined cron schedules (hourly, twicedaily, daily, etc.).
wp cron schedule list
# Test whether WP-Cron's HTTP loopback works.
wp cron test
Replacing WP-Cron with a system cron
First, disable the built-in WP-Cron trigger in wp-config.php:
define('DISABLE_WP_CRON', true);
Then add a line to your server's crontab:
# Runs every minute. WP-CLI executes only the events that are actually due.
* * * * * /usr/local/bin/wp --path=/var/www/html cron event run --due-now >/dev/null 2>&1
The --due-now flag is the key: it checks which events have passed their scheduled time and runs only those. Running wp cron event run --due-now every minute is safe and idempotent.
WP-CLI cron runs in a CLI process, not via HTTP. This is a separate execution path from WP-Cron's internal HTTP POST to wp-cron.php. PHP errors surface in the terminal (or crontab mail) instead of being swallowed by a background HTTP request.
Source: wp cron commands.
Cache and transients
# Flush the object cache.
wp cache flush
# Report which cache backend is active (default, Redis, Memcached, etc.).
wp cache type
# Delete all transients from the database.
wp transient delete --all
When wp cache flush does nothing. Without a persistent cache drop-in (Redis, Memcached), WordPress's object cache lives in PHP memory for the current process only. Running wp cache flush clears the CLI process's in-memory cache, which is discarded when the command exits anyway. In that scenario, wp cache flush is a no-op. It only has a real effect when a persistent cache drop-in is installed, in which case it flushes the shared backend and affects all requests site-wide.
In WP-CLI 2.12.0, both wp cache and wp transient gained pluck and patch subcommands for reading and modifying nested values inside cached arrays without fetching and re-setting the entire blob.
Source: wp cache commands, wp transient commands.
Health checks with wp doctor
wp doctor is not bundled with WP-CLI. Install it as a package first:
wp package install wp-cli/doctor-command
Then run it:
# Run all default diagnostic checks.
wp doctor check --all
The default check suite covers 13 items:
| Check | What it flags |
|---|---|
autoload-options-size |
Autoloaded options exceed 900 KB (a common WordPress performance problem). |
constant-savequeries-falsy |
SAVEQUERIES is true in production (memory leak). |
constant-wp-debug-falsy |
WP_DEBUG is true in production (error output exposed). |
core-update |
WordPress is out of date. |
core-verify-checksums |
Core files have been modified. |
cron-count |
More than 50 cron events queued. |
cron-duplicates |
More than 10 duplicate cron events. |
file-eval |
Suspicious eval() patterns in theme/plugin files. |
option-blog-public |
blog_public discourages search engines. |
plugin-active-count |
80+ plugins active. |
plugin-deactivated |
40%+ of plugins are deactivated (cleanup opportunity). |
plugin-update |
Pending plugin updates. |
theme-update |
Pending theme updates. |
Each check returns success, warning, or error. Custom check suites can be defined in a doctor.yml file.
Source: WP-CLI Doctor handbook, doctor-command on GitHub.
Running WP-CLI in Docker
Docker containers typically run as root. WP-CLI refuses to run as root by default and prints a security warning: "any code within your WordPress instance will have full privileges to the entire server." Two ways to suppress it:
Option 1: the --allow-root flag per command.
docker exec wordpress-app wp --allow-root plugin update --all
Option 2: the WP_CLI_ALLOW_ROOT environment variable (cleaner for Docker).
Set it in your compose.yaml or Dockerfile so every wp invocation inside the container skips the warning automatically:
# compose.yaml snippet
services:
wpcli:
image: wordpress:cli
environment:
WP_CLI_ALLOW_ROOT: "1"
volumes:
- wordpress-data:/var/www/html
The --allow-root flag does not grant additional privileges. It only suppresses the warning. The WordPress Docker tutorial covers a full Compose setup with WP-CLI as a profiled service.
Running as a non-root user is always preferable. In Docker, this means adding a user: directive in the Compose file or a USER instruction in the Dockerfile. The container sandbox is not a security boundary in every orchestration setup, and running as root inside the container becomes a real privilege escalation vector if the container can reach the host.
Source: WP-CLI GitHub issue #4548.
Useful aliases and shell scripts
Weekly maintenance script
#!/usr/bin/env bash
# wp-maintenance.sh - run weekly via cron or manually.
set -euo pipefail
WP_PATH="/var/www/html"
echo "=== Core integrity check ==="
wp --path="$WP_PATH" core verify-checksums
echo "=== Plugin updates ==="
wp --path="$WP_PATH" plugin update --all
echo "=== Theme updates ==="
wp --path="$WP_PATH" theme update --all
echo "=== Database optimization ==="
wp --path="$WP_PATH" db optimize
echo "=== Cache flush ==="
wp --path="$WP_PATH" cache flush
echo "=== Doctor check ==="
wp --path="$WP_PATH" doctor check --all
Shell aliases
Add these to ~/.bashrc or ~/.zshrc:
# Quick site info.
alias wpinfo='wp --info && wp core version && wp plugin list --format=table'
# Safe search-replace (dry-run only).
alias wpsr-dry='wp search-replace --all-tables-with-prefix --skip-columns=guid --dry-run'
# Export with datestamp.
alias wpbackup='wp db export backup-$(date +%Y%m%d-%H%M%S).sql'
Listing plugins with pending updates as JSON
wp plugin list --update=available --format=json | jq '.[].name'
This is useful for feeding into monitoring or notification systems. The --format=json flag works on virtually every wp ... list command.
Common misconceptions
"WP-CLI requires SSH access." It does not. WP-CLI is a PHP command-line tool that runs in any shell session: an SSH terminal, a docker exec shell, a vagrant ssh session, a kubectl exec pod, or a hosting panel's built-in terminal. Many managed WordPress hosts (Kinsta, WP Engine, Pantheon) offer WP-CLI through their control panel without the user configuring SSH at all. WP-CLI's --ssh flag lets you proxy commands to a remote server over SSH, but local usage needs no SSH whatsoever.
"wp search-replace without --precise is fine for most sites." It is not. Without --precise, WP-CLI uses SQL-level string replacement and only switches to PHP processing when it detects serialised data through column-type heuristics. Those heuristics can miss serialised blobs in non-standard columns, silently corrupting them. The --precise flag forces PHP-level processing for every row. It is slower, but on databases under a few gigabytes the difference is negligible. Always use --precise for migrations.
"WP-CLI's cron commands use the same path as WP-Cron." They do not. WP-Cron works by spawning an HTTP POST to wp-cron.php on every page load. wp cron event run executes events directly in the CLI process, with no HTTP request involved. This is a completely separate execution path. PHP fatal errors that WP-Cron swallows silently appear in the terminal when you run wp cron event run. This separation is exactly why the system cron replacement pattern is more reliable.