Engineering
WP-CLI: The Commands I Keep Coming Back To

Part 1 of the thread The WordPress toolbox
- WP-CLI runs a WordPress site from a terminal. Anything you'd click through in wp-admin, you can script.
- The everyday set is small: posts, plugins, users, options, and
wp db exportbefore anything risky. - A few commands are destructive by design.
wp site empty --uploadsisn't a health check, whatever my old notes said. - Pass
--rawtowp config setfor true/false values, and--parenttakes a term ID, not a slug.
I kept a long WP-CLI cheat sheet in my notes for years. Going back through it against the current docs, most of it held up. A handful of lines were wrong, and two of them would have wrecked a site if I'd run them the way they were written. This is the trimmed-down version, with those fixed.
Checked against the WP-CLI docs in September 2026.
Getting it running
WP-CLI is a single PHP file. Download wp-cli.phar from the official builds repo, and check it with wp --info. It needs PHP 7.2.24 or newer.
On Windows there's no chmod +x, so the docs suggest a tiny batch file named wp.bat next to the phar, with the folder on your PATH:
@ECHO OFF
php "C:/wp-cli/wp-cli.phar" %*
Then cd into any WordPress folder and every command below works against that site. For a site on a remote host, you'd SSHSecure Shell. An encrypted way to get a command line on another machine over the network. in and run the same commands there.
The everyday set
| Job | Command |
|---|---|
| List drafts | wp post list --post_status=draft --fields=ID,post_title |
| Create a post | wp post create --post_title="Holiday hours" --post_status=draft |
| Update a post | wp post update 123 --post_title="New title" |
| Plugins needing updates | wp plugin list --update=available |
| Update everything | wp plugin update --all and wp theme update --all |
| Add a user | wp user create sam [email protected] --role=editor |
| Change the site title | wp option update blogname "Example Bakery" |
| Back up the database | wp db export --add-drop-table |
| Fix broken permalinks | wp rewrite flush |
The trick that makes WP-CLI feel like a real tool is --format=ids. It prints bare post IDs, which you can hand straight to another command:
wp post list --post_type=post --post_status=draft --format=ids
for id in $(wp post list --post_type=post --format=ids); do wp post term add "$id" category news; done
wp post delete $(wp post list --post_type=post --post_status=trash --format=ids)
Some commands, like wp post delete, take a whole list of IDs at once. Others, like wp post term add, want one post at a time, so they get a loop. That last line empties the trash, by the way: a post that's already in the trash is deleted for good.
wp post list passes most arguments through to WP_Query, so things like --meta_key and --meta_value work too.
The ones that bite
These are the lines I had to correct.
wp site empty --uploads was sitting in my notes under "site health check." It is not one. It deletes every post, comment and term on the site, and --uploads deletes the media files too. Users and settings survive. Nothing else does.
wp db query "UPDATE wp_posts SET post_status = 'draft' ..." with no WHERE on the ID quietly unpublishes every post you have. If you need raw SQL, export the database first and test on a copy.
wp post delete 123 sends the post to the trash. Add --force and it's gone for good, which is what my old bulk-delete line did to every draft at once.
wp config set WP_DEBUG true writes the string 'true' instead of the boolean. It happens to work for true, but wp config set WP_DEBUG false writes the string 'false', which PHP treats as on. Pass --raw for true, false and numbers.
wp term create category "Business" --parent=technology fails, because --parent wants the parent's term ID. Create the parent with --porcelain to get its ID back. And wp term create makes one term per run, so my line that tried to create four tags from one comma-separated string made a single tag with commas in its name.
Warning
Anything with
--force,--all,resetoremptyin it deserves awp db exportfirst. It takes seconds, and it's the difference between a bad afternoon and a bad week.
Quick health checks
These are all read-only, or close to it:
wp core verify-checksums
wp plugin verify-checksums --all
wp core check-update
wp transient delete --expired
wp cron event list
verify-checksums compares your files against the WordPress.org originals, so it's the fastest way to spot a hacked core file. The plugin version only knows about plugins hosted on WordPress.org, so premium plugins show up as "could not verify." That's expected.
Old notes also mentioned wp profile hook --spotlight for finding slow hooks. wp profile is a separate package (wp package install wp-cli/profile-command), not part of the standard install.
Tip
--dry-runexists on the scariest command of all,wp search-replace. I cover that one properly in looking after a WordPress database, since getting it wrong breaks serialized settings in ways that don't show up right away.
When a job has to happen from somewhere without shell access, like a scheduled task on a Windows box, the REST API covers a lot of the same ground over HTTPS. And before handing WP-CLI to anyone else, lock down the admin accounts it can create.
- Locking Down WordPress Admin AccountsEngineering
- The Dev Toolbox for a WordPress BuildResources & Recommendations
- Looking After a WordPress Database: Backups, Cleanup and Search-Replace Done SafelyEngineering
- The WordPress REST API Without a Plugin: Reading, Creating and Updating ContentEngineering