I remember the dread of a Friday afternoon deploy. FTPing files, maybe an rsync -avz if I was feeling fancy, then SSHing in to run a wp cache flush. More often than not, I’d forget wp db update after a schema change in my plugin, or miss a composer install for a new dependency. That led to bizarre frontend errors, or worse, backend failures that only surfaced when an admin tried to save something. The frantic rollback, the lost 15 minutes of trying to remember which files I had actually updated. It felt like I was defusing a bomb every time.
That’s why I’m evangelical about proper deploy pipelines using WP-CLI and GitHub Actions for any WordPress project that matters. We moved to this setup about two years ago for a complex e-commerce integration, and it’s been a game changer. The initial setup was a push, but the headaches it saved later are immeasurable.
Here’s the core idea: your GitHub repository is the source of truth. When code lands on main, a GitHub Actions workflow kicks off. This workflow connects to your server via SSH, pulls the latest code, runs WP-CLI commands, and handles any Composer dependencies.
A typical workflow for our custom CRM plugin, for instance, looks something like this: first, we make sure the server has PHP and Composer. Then, the workflow clones the repository. Next, composer install --no-dev runs to grab all our backend library dependencies. This is crucial. I once spent an hour debugging a production site because a new class wasn’t found; turned out I’d forgotten to commit the vendor directory and the automated composer install was never set up for that particular deployment target. We learned that lesson hard.
After dependencies, WP-CLI takes over. wp cache flush --all is usually the first thing, clearing any persistent object cache or transient data that might hold stale information. Then, if our plugin has any schema changes, wp db update runs. This is non-negotiable. I can’t stress enough how many times I personally overlooked a db_schema_version increment in a custom table definition, a simple dbDelta call, leading to functions trying to access non-existent columns. The site just wouldn’t work right until I manually ran the command. Automating it prevents that whole class of error. We even have wp rewrite flush for any custom post type or taxonomy slug changes, which avoids the dreaded 404s after a deploy.
The key to all of this, especially with GitHub Actions, is secure SSH access and environment variables. You set up an SSH key on your server (read-only for deployments if possible), add the private key as a GitHub Secret, and then your workflow uses that to connect. This means no FTP credentials lying around, no shared passwords. It’s a clean, auditable process.
Some might argue that building a full artifact, zipping the plugin with vendor already included, and deploying that is cleaner than composer install on the server. And yes, it absolutely can be, especially for very large projects or multiple environments. For us, with dedicated staging and production servers that are relatively robust, running composer install directly has been fine and slightly simpler to configure in the workflow. The trade-off is slightly longer deploy times, but we’re talking seconds, not minutes.
The biggest win? Our deployment-related bug reports dropped by 80% within the first month. Our internal “rollback” count, where we had to revert a deploy entirely, went from 3-4 times a month to practically zero. That’s real time saved, real stress avoided. If you’re not automating your WordPress deploys with WP-CLI and GitHub Actions, you’re leaving stability and developer sanity on the table.