Manually deploying WordPress changes is risky, time-consuming, and prone to human error. Push to FTP, run SQL updates, clear caches, pray nothing breaks. When something goes wrong at 2 AM, there’s no easy rollback.
- What is CI/CD for WordPress?
- Prerequisites
- GitHub Actions Basics
- Setting Up Your Repository
- WordPress Testing Environment
- Automated Testing with WP-CLI
- Building the Deployment Workflow
- Environment Secrets Management
- Staging Deployment
- Production Deployment
- Database Migrations
- Rollback Strategy
- Complete GitHub Actions Workflow
- Best Practices
- Troubleshooting
- Next Steps
- Conclusion
Professional WordPress development requires automated CI/CD (Continuous Integration/Continuous Deployment) pipelines that test, build, and deploy code safely and consistently.
In this advanced guide, you’ll learn to build a production-ready WordPress CI/CD pipeline using GitHub Actions and WP-CLI. You’ll implement automated testing, staging deployments, database migrations, and zero-downtime production deploymentsβall triggered automatically by Git pushes.
By the end, you’ll have a professional WordPress DevOps workflow that eliminates deployment fear.
What is CI/CD for WordPress?
CI/CD Defined
Continuous Integration (CI):
- Automatically test code changes
- Verify WordPress functionality
- Run security checks
- Ensure code quality
Continuous Deployment (CD):
- Automatically deploy to staging
- Deploy to production on approval
- Run database migrations
- Zero-downtime deployments
The Manual Deployment Problem
Traditional workflow:
- Make changes locally
- FTP files to server (hope you don’t overwrite something)
- SSH and run SQL updates manually
- Clear caches
- Hope nothing is broken
- If broken, scramble to fix or revert
Problems:
- Downtime during deployment
- Human error (forgot to run migration, uploaded wrong file)
- No testing before production
- Difficult rollback
- No audit trail
CI/CD Workflow
Automated workflow:
- Push code to GitHub
- CI: Automated tests run
- CD: Deploy to staging automatically
- QA tests staging environment
- Approve production deployment
- CD: Deploy to production with zero downtime
- Automatic rollback if issues detected
Benefits:
- No manual deployment steps
- Every deployment is tested
- Instant rollback capability
- Complete audit trail in Git
- Confidence to deploy frequently
Prerequisites
Before building your CI/CD pipeline:
Required Accounts
- GitHub Account – For repository and Actions
- WordPress Server(s) – Staging and production with SSH access
- WP-CLI Access – On all WordPress servers
Server Requirements
Both staging and production need:
- SSH access
- WP-CLI installed –Β Installation guide
- Git installed
- Web server (Apache/Nginx)
- MySQL/MariaDB database
Local Development
- Git installed
- Basic understanding of WordPress development
- Bash scripting knowledge
Knowledge Requirements
Helpful background:
- Git basics (branches, commits, pull requests)
- GitHub fundamentals
- WordPress theme/plugin development
- SSH and server administration
GitHub Actions Basics
What are GitHub Actions?
GitHub Actions are automated workflows that run when specific GitHub events occur (push, pull request, release, etc.).
Workflow anatomy:
name: WordPress CI/CD
on:
push:
branches: [main, develop]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: ./run-tests.sh
Key concepts:
- Workflow: YAML file defining automation
- Event: Trigger (push, pull_request, schedule)
- Job: Set of steps that run on a runner
- Step: Individual task (run command, use action)
- Runner: Server that runs workflows (GitHub-hosted or self-hosted)
Why GitHub Actions for WordPress?
Advantages:
- Free for public repos (2,000 minutes/month for private)
- Integrated with GitHub (no external CI service)
- Large marketplace of pre-built actions
- Supports matrix builds (test multiple PHP versions)
- Secret management built-in
Learn more: GitHub Actions Documentation
Setting Up Your Repository
Repository Structure
Organize your WordPress project for CI/CD:
wordpress-project/
βββ .github/
β βββ workflows/
β βββ ci.yml # Continuous Integration
β βββ deploy.yml # Continuous Deployment
βββ wp-content/
β βββ themes/
β β βββ my-theme/
β βββ plugins/
β β βββ my-plugin/
β βββ mu-plugins/
βββ scripts/
β βββ deploy.sh # Deployment script
β βββ test.sh # Testing script
β βββ migrate.sh # Database migration
βββ tests/
β βββ test-wordpress.php
βββ composer.json
βββ README.md
What to version control:
- β Custom themes
- β Custom plugins
- β mu-plugins
- β Configuration files
- β Deployment scripts
What NOT to version:
- β WordPress core (use Composer or WP-CLI)
- β Third-party plugins (manage separately)
- β
wp-config.php(use environment-specific configs) - β
uploads/directory - β
.envfiles with secrets
Create .gitignore
# .gitignore for WordPress
# WordPress core
/wp-admin/
/wp-includes/
/wp-*.php
/xmlrpc.php
/license.txt
/readme.html
# Configuration
wp-config.php
.env
.env.*
# Uploads
wp-content/uploads/
# Cache
wp-content/cache/
wp-content/backup*/
# Third-party plugins (manage via Composer)
wp-content/plugins/*
!wp-content/plugins/my-custom-plugin/
# IDE
.vscode/
.idea/
# OS
.DS_Store
Thumbs.db
# Dependencies
/vendor/
node_modules/
Initialize Repository
# Create repository
git init
git add .
git commit -m "Initial commit"
# Create and push to GitHub
gh repo create my-wordpress-site --public --source=. --remote=origin --push
# Or manually
git remote add origin git@github.com:username/my-wordpress-site.git
git push -u origin main
WordPress Testing Environment
Set Up WordPress in GitHub Actions
Use the setup-wordpress action to create a test environment:
# .github/workflows/ci.yml
name: WordPress CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:5.7
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress_test
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
extensions: mysql, mbstring, xml
tools: wp-cli
- name: Install WordPress
run: |
wp core download --path=/tmp/wordpress
wp config create --dbname=wordpress_test --dbuser=root --dbpass=root --dbhost=127.0.0.1 --path=/tmp/wordpress
wp core install --url=http://localhost --title="Test Site" --admin_user=admin --admin_password=admin --admin_email=admin@test.com --path=/tmp/wordpress
- name: Activate plugin/theme
run: |
# Copy your custom code
cp -r $GITHUB_WORKSPACE/wp-content/plugins/my-plugin /tmp/wordpress/wp-content/plugins/
wp plugin activate my-plugin --path=/tmp/wordpress
- name: Run tests
run: |
./scripts/test.sh
Create Test Script
#!/bin/bash
# scripts/test.sh
set -e
WP_PATH="/tmp/wordpress"
echo "Running WordPress tests..."
# Test 1: WordPress is installed
wp core is-installed --path=$WP_PATH
# Test 2: Database is accessible
wp db check --path=$WP_PATH
# Test 3: Plugin is active
wp plugin is-active my-plugin --path=$WP_PATH
# Test 4: WordPress version
version=$(wp core version --path=$WP_PATH)
echo "WordPress version: $version"
# Test 5: Run PHP CodeSniffer (if configured)
if command -v phpcs &> /dev/null; then
phpcs --standard=WordPress wp-content/plugins/my-plugin/
fi
echo "β All tests passed!"
Make it executable:
chmod +x scripts/test.sh
git add scripts/test.sh
git commit -m "Add test script"
Automated Testing with WP-CLI
Test Categories
1. Sanity Tests
- WordPress is installed
- Database connection works
- Required plugins are active
2. Functionality Tests
- Custom plugin functions work
- Theme renders correctly
- Custom post types exist
3. Security Tests
- No vulnerable plugins
- WordPress core integrity
- File permissions
4. Performance Tests
- Database query count
- Page load time
- Cache functionality
Comprehensive Test Script
#!/bin/bash
# scripts/test.sh - Comprehensive WordPress testing
set -e
WP_PATH=${WP_PATH:-/tmp/wordpress}
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
test_count=0
passed=0
failed=0
run_test() {
local test_name=$1
local test_command=$2
((test_count++))
echo -n "Test $test_count: $test_name... "
if eval "$test_command" &>/dev/null; then
echo -e "${GREEN}PASS${NC}"
((passed++))
return 0
else
echo -e "${RED}FAIL${NC}"
((failed++))
return 1
fi
}
echo "========================================="
echo "WordPress Automated Testing"
echo "========================================="
# Sanity tests
run_test "WordPress installed" "wp core is-installed --path=$WP_PATH"
run_test "Database accessible" "wp db check --path=$WP_PATH"
run_test "Core files verified" "wp core verify-checksums --path=$WP_PATH"
# Plugin tests
run_test "Custom plugin active" "wp plugin is-active my-plugin --path=$WP_PATH"
run_test "No plugin errors" "wp plugin list --path=$WP_PATH --status=active --format=count"
# Theme tests
run_test "Theme activated" "wp theme is-active my-theme --path=$WP_PATH"
# Security tests
run_test "No vulnerable plugins" "! wp plugin list --path=$WP_PATH --field=update | grep -q available"
# Performance tests
run_test "Cache object working" "wp cache flush --path=$WP_PATH"
# Database tests
run_test "Database optimized" "wp db optimize --path=$WP_PATH"
echo "========================================="
echo "Test Results: $passed passed, $failed failed"
echo "========================================="
if [ $failed -gt 0 ]; then
exit 1
fi
exit 0
Matrix Testing (Multiple PHP Versions)
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['7.4', '8.0', '8.1']
wordpress-version: ['6.2', '6.3', '6.4']
name: PHP ${{ matrix.php-version }} / WP ${{ matrix.wordpress-version }}
steps:
- uses: actions/checkout@v3
- name: Setup PHP ${{ matrix.php-version }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
- name: Install WordPress ${{ matrix.wordpress-version }}
run: |
wp core download --version=${{ matrix.wordpress-version }} --path=/tmp/wordpress
# ... rest of setup
- name: Run tests
run: ./scripts/test.sh
This tests across 9 combinations (3 PHP Γ 3 WP versions).
Building the Deployment Workflow
Deployment Script Structure
#!/bin/bash
# scripts/deploy.sh
set -euo pipefail
ENVIRONMENT=$1 # staging or production
SERVER=$2 # server hostname
WP_PATH=$3 # WordPress path on server
echo "Deploying to $ENVIRONMENT ($SERVER)"
# 1. Create backup
ssh $SERVER "cd $WP_PATH && wp db export backup-$(date +%s).sql"
# 2. Pull latest code
ssh $SERVER "cd $WP_PATH && git pull origin main"
# 3. Install dependencies
ssh $SERVER "cd $WP_PATH && composer install --no-dev"
# 4. Run database migrations
ssh $SERVER "cd $WP_PATH && wp cli migrate"
# 5. Clear caches
ssh $SERVER "cd $WP_PATH && wp cache flush"
ssh $SERVER "cd $WP_PATH && wp rewrite flush"
# 6. Verify deployment
ssh $SERVER "cd $WP_PATH && wp core is-installed"
echo "β Deployment complete!"
SSH Key Setup
Add SSH private key to GitHub Secrets:
- Generate SSH key (if needed):
ssh-keygen -t ed25519 -C "github-actions@example.com" -f ~/.ssh/github-actions
- Add public key to server:
ssh-copy-id -i ~/.ssh/github-actions.pub user@your-server.com
- Add private key to GitHub:
- Go to repo Settings β Secrets β Actions
- New repository secret
- Name:
SSH_PRIVATE_KEY - Value: Contents of
~/.ssh/github-actions(private key)
Environment Secrets Management
GitHub Secrets
Store sensitive data in GitHub Secrets:
Required secrets:
SSH_PRIVATE_KEY– SSH key for server accessSSH_HOST_STAGING– Staging server hostnameSSH_HOST_PRODUCTION– Production server hostnameSSH_USER– SSH usernameWP_PATH_STAGING– WordPress path on stagingWP_PATH_PRODUCTION– WordPress path on production
Using Secrets in Workflows
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup SSH
uses: webfactory/ssh-agent@v0.7.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Deploy to staging
run: |
./scripts/deploy.sh \
staging \
${{ secrets.SSH_HOST_STAGING }} \
${{ secrets.WP_PATH_STAGING }}
Environment Variables
Create environment-specific configurations:
jobs:
deploy-staging:
environment: staging
env:
WP_ENV: staging
SSH_HOST: ${{ secrets.SSH_HOST_STAGING }}
WP_PATH: ${{ secrets.WP_PATH_STAGING }}
deploy-production:
environment: production
env:
WP_ENV: production
SSH_HOST: ${{ secrets.SSH_HOST_PRODUCTION }}
WP_PATH: ${{ secrets.WP_PATH_PRODUCTION }}
Staging Deployment
Auto-Deploy to Staging
Deploy automatically on push to develop branch:
# .github/workflows/deploy-staging.yml
name: Deploy to Staging
on:
push:
branches: [develop]
jobs:
deploy:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v3
- name: Setup SSH
uses: webfactory/ssh-agent@v0.7.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Deploy to staging server
run: |
ssh -o StrictHostKeyChecking=no ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST_STAGING }} << 'ENDSSH'
set -e
cd ${{ secrets.WP_PATH_STAGING }}
# Backup database
wp db export backup-$(date +%Y%m%d-%H%M%S).sql
# Pull latest code
git fetch origin
git reset --hard origin/develop
# Install dependencies
composer install --no-dev --optimize-autoloader
# Database migrations
if [ -f scripts/migrate.sh ]; then
./scripts/migrate.sh
fi
# Clear caches
wp cache flush
wp rewrite flush
# Verify
wp core is-installed && echo "Deployment successful!"
ENDSSH
- name: Notify on Slack
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'Staging deployment: ${{ job.status }}'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
Production Deployment
Manual Approval Required
Require manual approval before production deployment:
# .github/workflows/deploy-production.yml
name: Deploy to Production
on:
workflow_dispatch: # Manual trigger only
release:
types: [published]
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
url: https://yoursite.com
steps:
- uses: actions/checkout@v3
- name: Setup SSH
uses: webfactory/ssh-agent@v0.7.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Pre-deployment checks
run: |
# Verify staging is working
curl -f https://staging.yoursite.com || exit 1
- name: Create backup
run: |
ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST_PRODUCTION }} \
"cd ${{ secrets.WP_PATH_PRODUCTION }} && wp db export backups/pre-deploy-$(date +%s).sql"
- name: Deploy to production
run: |
ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST_PRODUCTION }} << 'ENDSSH'
set -e
cd ${{ secrets.WP_PATH_PRODUCTION }}
# Enable maintenance mode
wp maintenance-mode activate
# Pull latest code
git fetch origin
git reset --hard origin/main
# Install dependencies
composer install --no-dev --optimize-autoloader
# Database migrations
if [ -f scripts/migrate.sh ]; then
./scripts/migrate.sh
fi
# Clear caches
wp cache flush
wp rewrite flush
wp transient delete --all
# Disable maintenance mode
wp maintenance-mode deactivate
# Verify
wp core is-installed && echo "Production deployment successful!"
ENDSSH
- name: Smoke tests
run: |
# Test homepage
curl -f https://yoursite.com
# Test critical pages
curl -f https://yoursite.com/about
curl -f https://yoursite.com/contact
- name: Rollback on failure
if: failure()
run: |
echo "Deployment failed, initiating rollback..."
ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST_PRODUCTION }} \
"cd ${{ secrets.WP_PATH_PRODUCTION }} && git reset --hard HEAD~1"
Protected Branches
Configure branch protection in GitHub:
- Go to Settings β Branches
- Add rule for
mainbranch - Enable:
- Require pull request reviews (1+ approvals)
- Require status checks to pass (CI tests)
- Require branches to be up to date
This ensures production only receives tested, reviewed code.
Database Migrations
Migration Script Pattern
#!/bin/bash
# scripts/migrate.sh
set -e
WP_PATH=${WP_PATH:-.}
echo "Running database migrations..."
# Migration 001: Add custom table
wp db query "
CREATE TABLE IF NOT EXISTS wp_custom_data (
id bigint(20) NOT NULL AUTO_INCREMENT,
user_id bigint(20) NOT NULL,
data longtext NOT NULL,
created_at datetime NOT NULL,
PRIMARY KEY (id)
);
" --path=$WP_PATH
# Migration 002: Update post meta
wp post meta update-all custom_field new_value --from=old_value --path=$WP_PATH
# Migration 003: Create custom taxonomy
wp taxonomy register custom_taxonomy --path=$WP_PATH
echo "β Migrations complete!"
Version-Controlled Migrations
Use migration files:
# migrations/001-create-custom-table.sql
CREATE TABLE IF NOT EXISTS wp_custom_data (
id bigint(20) NOT NULL AUTO_INCREMENT,
data longtext NOT NULL,
PRIMARY KEY (id)
);
# migrations/002-add-meta-field.sh
#!/bin/bash
wp post list --format=ids | xargs -I {} wp post meta update {} new_field default_value
Migration runner:
#!/bin/bash
# scripts/migrate.sh
WP_PATH=${WP_PATH:-.}
MIGRATIONS_DIR="migrations"
for migration in $MIGRATIONS_DIR/*; do
echo "Running migration: $(basename $migration)"
if [[ $migration == *.sql ]]; then
wp db query < $migration --path=$WP_PATH
elif [[ $migration == *.sh ]]; then
bash $migration
fi
echo "β Completed: $(basename $migration)"
done
Rollback Strategy
Automated Rollback on Failure
- name: Deploy with rollback
id: deploy
run: |
# Get current commit for rollback
CURRENT_COMMIT=$(ssh $SERVER "cd $WP_PATH && git rev-parse HEAD")
echo "current_commit=$CURRENT_COMMIT" >> $GITHUB_OUTPUT
# Deploy
./scripts/deploy.sh
- name: Verify deployment
id: verify
run: |
# Run smoke tests
curl -f https://yoursite.com || exit 1
ssh $SERVER "cd $WP_PATH && wp core is-installed" || exit 1
- name: Rollback on failure
if: failure() && steps.deploy.outcome == 'success'
run: |
echo "Deployment verification failed, rolling back..."
# Restore database backup
BACKUP_FILE=$(ssh $SERVER "ls -t $WP_PATH/backups/*.sql | head -1")
ssh $SERVER "cd $WP_PATH && wp db import $BACKUP_FILE"
# Restore code
ssh $SERVER "cd $WP_PATH && git reset --hard ${{ steps.deploy.outputs.current_commit }}"
# Clear caches
ssh $SERVER "cd $WP_PATH && wp cache flush"
echo "β Rollback complete"
exit 1
Manual Rollback Workflow
# .github/workflows/rollback.yml
name: Rollback Production
on:
workflow_dispatch:
inputs:
commit_sha:
description: 'Commit SHA to rollback to'
required: true
jobs:
rollback:
runs-on: ubuntu-latest
environment: production
steps:
- name: Confirm rollback
run: |
echo "Rolling back to commit: ${{ github.event.inputs.commit_sha }}"
- name: Rollback code
run: |
ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST_PRODUCTION }} \
"cd ${{ secrets.WP_PATH_PRODUCTION }} && git reset --hard ${{ github.event.inputs.commit_sha }}"
- name: Restore database
run: |
# Restore from backup (manual selection required)
echo "Please restore database manually from backups/"
Complete GitHub Actions Workflow
Production-Ready CI/CD
# .github/workflows/wordpress-cicd.yml
name: WordPress CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
workflow_dispatch:
env:
PHP_VERSION: '8.1'
jobs:
# Job 1: Run tests
test:
name: Test
runs-on: ubuntu-latest
services:
mysql:
image: mysql:5.7
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress_test
ports:
- 3306:3306
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
extensions: mysql, mbstring, xml
tools: wp-cli, composer
- name: Install WordPress
run: |
wp core download --path=/tmp/wordpress
wp config create \
--dbname=wordpress_test \
--dbuser=root \
--dbpass=root \
--dbhost=127.0.0.1 \
--path=/tmp/wordpress
wp core install \
--url=http://localhost \
--title="Test Site" \
--admin_user=admin \
--admin_password=admin \
--admin_email=admin@test.com \
--path=/tmp/wordpress
- name: Copy custom code
run: |
cp -r wp-content/themes/* /tmp/wordpress/wp-content/themes/
cp -r wp-content/plugins/* /tmp/wordpress/wp-content/plugins/
- name: Run tests
run: ./scripts/test.sh
env:
WP_PATH: /tmp/wordpress
# Job 2: Deploy to staging
deploy-staging:
name: Deploy to Staging
needs: test
if: github.ref == 'refs/heads/develop' && github.event_name == 'push'
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v3
- name: Setup SSH
uses: webfactory/ssh-agent@v0.7.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Deploy
run: |
./scripts/deploy.sh \
staging \
${{ secrets.SSH_HOST_STAGING }} \
${{ secrets.WP_PATH_STAGING }}
# Job 3: Deploy to production
deploy-production:
name: Deploy to Production
needs: test
if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
environment:
name: production
url: https://yoursite.com
steps:
- uses: actions/checkout@v3
- name: Setup SSH
uses: webfactory/ssh-agent@v0.7.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Deploy
id: deploy
run: |
./scripts/deploy.sh \
production \
${{ secrets.SSH_HOST_PRODUCTION }} \
${{ secrets.WP_PATH_PRODUCTION }}
- name: Verify
run: |
curl -f https://yoursite.com || exit 1
- name: Notify
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'Production deployment: ${{ job.status }}'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
Best Practices
Security
- Never commit secrets – Use GitHub Secrets
- Restrict SSH access – Use dedicated deploy keys
- Audit deployments – Enable GitHub audit log
- Scan for vulnerabilities – Use GitHub’s Dependabot
Deployment
- Always test first – Run CI tests before deploy
- Use staging – Test on staging before production
- Backup before deploy – Automatic database backups
- Enable maintenance mode – During production deployments
- Monitor after deploy – Check logs and metrics
Code Quality
- Use linters – PHP CodeSniffer, ESLint
- Run static analysis – PHPStan, Psalm
- Code coverage – Track test coverage
- Peer review – Require PR approvals
Performance
- Cache workflows – Cache Composer dependencies
- Parallel jobs – Run tests in parallel
- Self-hosted runners – For faster deploys
Troubleshooting
Issue 1: SSH Connection Failed
Problem: Permission denied (publickey)
Solutions:
- Verify SSH key is added to GitHub Secrets correctly
- Test SSH connection:
ssh -i ~/.ssh/github-actions user@server.com
- Check server’s
~/.ssh/authorized_keys - Verify SSH agent setup in workflow
Issue 2: Deployment Timeout
Problem: Workflow times out during deployment
Solutions:
- Increase timeout:
jobs:
deploy:
timeout-minutes: 30 # Default is 360
- Optimize deployment script
- Use caching for dependencies
Issue 3: Database Migration Failed
Problem: Migration fails, breaks site
Solutions:
- Always backup before migrations
- Test migrations on staging first
- Use transactions when possible:
START TRANSACTION;
-- migration SQL
COMMIT;
- Implement rollback in migration script
Issue 4: Cache Not Cleared
Problem: Changes don’t appear after deployment
Solutions:
- Add cache clearing to deployment:
wp cache flush
wp rewrite flush
wp transient delete --all
- Clear CDN cache if using one
- Verify opcache is reset
Issue 5: Workflow Fails Silently
Problem: Workflow completes but deployment didn’t work
Solutions:
- Remove
set -etemporarily to see errors - Add verbose logging:
set -x # Print commands
- Check server logs:
ssh server "tail -f /var/log/apache2/error.log"
Next Steps
Congratulations! You’ve built a production-ready WordPress CI/CD pipeline.
Enhance Your Pipeline
- WordPress Multisite CI/CD – Deploy to network
- Automated Backups – Integrate with deployment
- Performance Testing – Add to CI
Related Guides
- Custom WP-CLI CommandsΒ – Build deployment commands
- Environment ManagementΒ – Advanced configs
- Bash FunctionsΒ – Improve scripts
Advanced Topics
Add to your pipeline:
- Visual regression testing (BackstopJS, Percy)
- Load testing (k6, Apache JBench)
- Security scanning (OWASP ZAP)
- Accessibility testing (pa11y)
- SEO validation
- Automated changelogs
Master WordPress DevOps
Want to build enterprise-grade WordPress automation?
Join WPCLI MasteryΒ and learn:
- Complete CI/CD pipeline templates
- Multi-environment WordPress architecture
- Blue-green deployments
- Automated rollbacks and disaster recovery
- Early bird course pricing ($99 vs $199)
Conclusion
Automated CI/CD pipelines eliminate deployment fear and enable rapid, confident WordPress development. The GitHub Actions workflow you built today provides:
- Automated testing on every push
- Safe staging deployments
- Controlled production releases
- Instant rollback capability
- Complete audit trail
Key takeaways:
- CI/CD catches errors before production
- GitHub Actions integrates seamlessly with WordPress
- WP-CLI makes WordPress automation possible
- Staging β Production workflow ensures safety
- Automated rollbacks prevent disasters
- Database migrations need careful handling
The CI/CD pipeline you implemented today is the foundation of professional WordPress DevOps. Deploy with confidence.
Ready to implement? Start with the complete workflow and adapt to your needs.
Questions about WordPress CI/CD? Drop a comment below!
Found this helpful? Share with WordPress developers building automation.
Next:Β ExploreΒ advanced WordPress deployment strategiesΒ and zero-downtime techniques.
