Skip to main content
Automated WordPress Plugin Deployment with GitHub Actions and WP-CLI

Automated WordPress Plugin Deployment with GitHub Actions and WP-CLI

Building robust WordPress applications often involves custom plugins and themes that require careful deployment.

Building robust WordPress applications often involves custom plugins and themes that require careful deployment. Manually FTPing files or even performing git pull operations directly on a server can be tedious, error-prone, and lack the necessary audit trail. This is where a proper CI/CD pipeline shines, allowing us to automate deployments directly from our version control system. Today, I want to walk through setting up a streamlined deployment pipeline for a custom WordPress plugin using GitHub Actions and WP-CLI.

Our goal is simple: whenever we push changes to the main branch of our custom plugin’s GitHub repository, we want those changes to be automatically deployed to our staging server. This ensures our staging environment is always up-to-date with the latest code, ready for testing.

First, let’s prepare our staging server. We’ll need a user with SSH access that has write permissions to the wp-content/plugins directory of your WordPress installation. This user should also have wp-cli installed and accessible in its PATH. For security, it’s best to create a dedicated deployment user on your server and configure it for key-based authentication, disabling password login. We’ll generate an SSH key pair for this user; the public key goes into ~/.ssh/authorized_keys on the server, and the private key will be stored securely in GitHub Secrets.

Now, let’s craft the GitHub Actions workflow. Create a file named .github/workflows/deploy.yml in your plugin’s repository.

name: Deploy Plugin to Staging

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

- name: Setup SSH
        uses: webfactory/[email protected]
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}

- name: Deploy plugin files via rsync
        run: |
          mkdir -p ~/.ssh
          ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
          rsync -avz --exclude '.git/' --exclude '.github/' --exclude 'node_modules/' \
            ./ ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.REMOTE_PLUGIN_PATH }}

- name: Flush WordPress cache
        run: |
          ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ secrets.REMOTE_WP_ROOT_PATH }} && wp cache flush"

Let’s break down this YAML. The on: push block triggers the workflow whenever changes are pushed to the main branch. The deploy job runs on ubuntu-latest.

1.  **Checkout code**: This step simply checks out your plugin's repository onto the GitHub Actions runner.
2.  **Setup SSH**: Here, we use a community action to set up the SSH agent with our private key. The `SSH_PRIVATE_KEY` secret will hold the private key content we generated earlier.
3.  **Deploy plugin files via rsync**: This is the core deployment step.
    *   `mkdir -p ~/.ssh` creates the SSH directory if it doesn't exist.
    *   `ssh-keyscan` adds the host key of your staging server to the GitHub Actions runner's `known_hosts` file, preventing "Are you sure you want to continue connecting?" prompts during `rsync` and `ssh` commands. `SSH_HOST` is another secret storing your server's IP address or hostname.
    *   `rsync` copies your plugin's entire directory (excluding `.git/`, `.github/`, and `node_modules/` to save bandwidth and prevent deploying unnecessary development files) to the `REMOTE_PLUGIN_PATH` on your server. `SSH_USER` holds the deployment username, and `REMOTE_PLUGIN_PATH` should be something like `/var/www/html/wp-content/plugins/your-plugin-slug`.
4.  **Flush WordPress cache**: After updating plugin files, it's crucial to flush any object or persistent caches WordPress might be using. We SSH back into the server, navigate to the WordPress root (`REMOTE_WP_ROOT_PATH`), and execute `wp cache flush` using WP-CLI.

To secure your sensitive information, head to your GitHub repository's Settings > Secrets > Actions and add the following repository secrets:
*   `SSH_PRIVATE_KEY`: The *entire* private SSH key (including `-----BEGIN OPENSSH PRIVATE KEY-----` and `-----END OPENSSH PRIVATE KEY-----`).
*   `SSH_HOST`: Your staging server's hostname or IP address (e.g., `your-staging-server.com`).
*   `SSH_USER`: The deployment username on your staging server (e.g., `deployuser`).
*   `REMOTE_PLUGIN_PATH`: The absolute path to your plugin's directory on the staging server (e.g., `/var/www/html/wp-content/plugins/my-custom-plugin`).
*   `REMOTE_WP_ROOT_PATH`: The absolute path to your WordPress root directory on the staging server (e.g., `/var/www/html`).

With this setup, every push to your main branch will automatically trigger the workflow, deploy your plugin, and flush the cache, providing a reliable and efficient deployment process. This foundation can be extended with additional steps like running composer install for PHP dependencies or npm build for frontend assets before rsync, but for a pure backend plugin, this minimal setup is robust and effective.