Deploying WordPress changes manually means FTPing files, manually importing databases, running SQL updates, and crossing your fingers that nothing breaks. One mistake during deployment takes your production site offline during peak traffic hours.
A WordPress deployment pipeline automates the entire processβcode changes flow from development through testing to production automatically, with safety checks, automated testing, rollback capability, and zero downtime. Deploy confidently dozens of times per day.
In this guide, youβll build a complete WordPress deployment pipeline using WP-CLI and Bash, from basic scripts to production-ready systems used by professional WordPress development teams.
Why Build a Deployment Pipeline?
Manual WordPress deployments donβt scale and introduce human error at every step.
Problems with Manual Deployment
Error-prone: Manual file transfers and database updates cause mistakes.
Downtime: Sites go offline during deployments affecting users and revenue.
No testing: Changes deploy directly to production without validation.
Slow: Manual deployments take 30-60 minutes of focused work.
No rollback: Fixing failed deployments requires manual restoration from backups.
Deployment Pipeline Benefits
Automated: One command deploys entire release from development to production.
Zero downtime: Users never see maintenance pages or errors.
Tested: Automated tests catch issues before they reach production.
Fast: Complete deployments in under 5 minutes instead of hours.
Reversible: Instant rollback to previous version if issues occur.
According to DevOps Research, teams with deployment pipelines deploy 200x more frequently with 24x faster recovery times.
Deployment Pipeline Components
Understand the stages of a WordPress deployment pipeline.
Basic Pipeline Stages
1. Development β Code changes made locally
2. Testing β Automated tests verify changes
3. Staging β Deploy to staging environment
4. Validation β Manual/automated verification
5. Production β Deploy to live site
6. Monitoring β Track deployment healthEssential Pipeline Elements
Version Control: Git tracks all changes and enables rollback.
Database Migrations: Automated schema and data updates.
Asset Building: Compile CSS, minify JavaScript, optimize images.
Environment Config: Different settings for dev/staging/production.
Health Checks: Verify deployment succeeded before completing.
Rollback Strategy: Quick recovery from failed deployments.
Learn about continuous deployment principles.
Basic Deployment Script
Start with a simple automated deployment workflow.
Manual Deployment Process
#!/bin/bash
# deploy-wordpress.sh - Basic deployment script
set -euo pipefail
REPO_URL="git@github.com:yourcompany/wordpress-site.git"
DEPLOY_PATH="/var/www/production"
BRANCH="main"
cd "$DEPLOY_PATH"
echo "Starting deployment..."
# Pull latest code
git fetch origin
git reset --hard "origin/$BRANCH"
# Install/update dependencies
composer install --no-dev --optimize-autoloader
# Run database migrations
wp db migrate
# Clear caches
wp cache flush
wp rewrite flush
# Build assets (if needed)
npm run build
echo "β Deployment complete"Deployment with Backup
#!/bin/bash
# deploy-with-backup.sh
set -euo pipefail
DEPLOY_PATH="/var/www/production"
BACKUP_DIR="/backups/deployments"
DATE=$(date +%Y%m%d_%H%M%S)
cd "$DEPLOY_PATH"
# Create pre-deployment backup
echo "Creating backup..."
mkdir -p "$BACKUP_DIR"
wp db export "$BACKUP_DIR/db-$DATE.sql.gz"
git rev-parse HEAD > "$BACKUP_DIR/git-commit-$DATE.txt"
# Deploy
echo "Deploying..."
git pull origin main
composer install --no-dev
# Database updates
wp db migrate || {
echo "Migration failed, rolling back..."
wp db import "$BACKUP_DIR/db-$DATE.sql.gz"
git reset --hard $(cat "$BACKUP_DIR/git-commit-$DATE.txt")
exit 1
}
# Clear caches
wp cache flush
echo "β Deployment complete"
echo "Backup: $BACKUP_DIR/db-$DATE.sql.gz"Staging to Production Pipeline
Deploy changes from staging environment to production safely.
Complete Staging Deploy
#!/bin/bash
# deploy-staging-to-production.sh
set -euo pipefail
STAGING_PATH="/var/www/staging"
PROD_PATH="/var/www/production"
BACKUP_DIR="/backups/production"
DATE=$(date +%Y%m%d_%H%M%S)
echo "=== Staging to Production Deployment ==="
# Step 1: Backup production
echo "Step 1/6: Backing up production..."
cd "$PROD_PATH"
wp db export "$BACKUP_DIR/db-$DATE.sql.gz"
tar -czf "$BACKUP_DIR/files-$DATE.tar.gz" \
wp-content/plugins \
wp-content/themes \
wp-content/uploads
echo "β Backup complete"
# Step 2: Get staging code version
echo "Step 2/6: Getting staging version..."
cd "$STAGING_PATH"
STAGING_COMMIT=$(git rev-parse HEAD)
echo "Staging commit: $STAGING_COMMIT"
# Step 3: Deploy code to production
echo "Step 3/6: Deploying code..."
cd "$PROD_PATH"
# Enable maintenance mode
wp maintenance-mode activate
git fetch origin
git reset --hard "$STAGING_COMMIT"
# Step 4: Install dependencies
echo "Step 4/6: Installing dependencies..."
composer install --no-dev --no-interaction
# Build assets
npm run build
# Step 5: Run database migrations
echo "Step 5/6: Running database migrations..."
if ! wp db migrate; then
echo "β Migration failed, rolling back..."
wp db import "$BACKUP_DIR/db-$DATE.sql.gz"
wp maintenance-mode deactivate
exit 1
fi
# Step 6: Final checks and cleanup
echo "Step 6/6: Final checks..."
# Verify WordPress works
if ! wp core is-installed; then
echo "β WordPress check failed, rolling back..."
wp db import "$BACKUP_DIR/db-$DATE.sql.gz"
wp maintenance-mode deactivate
exit 1
fi
# Clear caches
wp cache flush
wp rewrite flush
# Disable maintenance mode
wp maintenance-mode deactivate
echo "=== Deployment Complete ==="
echo "Deployed commit: $STAGING_COMMIT"
echo "Backup: $BACKUP_DIR/db-$DATE.sql.gz"Automated Validation
#!/bin/bash
# validate-deployment.sh
SITE_URL="$1"
echo "Validating deployment..."
# Test homepage
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$SITE_URL")
if [ "$HTTP_CODE" != "200" ]; then
echo "β Homepage failed: HTTP $HTTP_CODE"
exit 1
fi
echo "β Homepage accessible"
# Test admin
ADMIN_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$SITE_URL/wp-admin/")
if [ "$ADMIN_CODE" != "200" ] && [ "$ADMIN_CODE" != "302" ]; then
echo "β Admin failed: HTTP $ADMIN_CODE"
exit 1
fi
echo "β Admin accessible"
# Test critical pages
for PAGE in "/about" "/contact" "/shop"; do
CODE=$(curl -s -o /dev/null -w "%{http_code}" "$SITE_URL$PAGE")
if [ "$CODE" = "200" ]; then
echo "β $PAGE accessible"
else
echo "β $PAGE returned HTTP $CODE"
fi
done
echo "β Validation complete"Zero-Downtime Deployment
Deploy without taking the site offline.
Symlink Strategy
#!/bin/bash
# zero-downtime-deploy.sh
set -euo pipefail
DEPLOY_BASE="/var/www/releases"
CURRENT_LINK="/var/www/current"
SHARED_DIR="/var/www/shared"
RELEASE_ID=$(date +%Y%m%d_%H%M%S)
RELEASE_PATH="$DEPLOY_BASE/$RELEASE_ID"
echo "=== Zero-Downtime Deployment ==="
echo "Release ID: $RELEASE_ID"
# Create new release directory
mkdir -p "$RELEASE_PATH"
cd "$RELEASE_PATH"
# Clone code
git clone --depth 1 git@github.com:yourcompany/site.git .
# Link shared directories
ln -s "$SHARED_DIR/wp-content/uploads" wp-content/uploads
ln -s "$SHARED_DIR/wp-config.php" wp-config.php
# Install dependencies
composer install --no-dev
# Build assets
npm run build
# Database migrations (on shared database)
cd "$SHARED_DIR"
wp db migrate --path="$RELEASE_PATH"
# Atomic switch to new release
ln -sfn "$RELEASE_PATH" "$CURRENT_LINK"
# Webserver points to /var/www/current
# Cleanup old releases (keep last 5)
cd "$DEPLOY_BASE"
ls -t | tail -n +6 | xargs rm -rf
echo "β Deployment complete"
echo "Current release: $RELEASE_ID"Blue-Green Deployment
#!/bin/bash
# blue-green-deploy.sh
BLUE_PATH="/var/www/blue"
GREEN_PATH="/var/www/green"
CURRENT_LINK="/var/www/current"
# Determine inactive environment
if [ "$(readlink $CURRENT_LINK)" = "$BLUE_PATH" ]; then
INACTIVE="$GREEN_PATH"
INACTIVE_NAME="green"
else
INACTIVE="$BLUE_PATH"
INACTIVE_NAME="blue"
fi
echo "Deploying to inactive environment: $INACTIVE_NAME"
cd "$INACTIVE"
# Deploy to inactive
git pull origin main
composer install --no-dev
npm run build
# Test inactive environment
if curl -f "http://localhost:8080" > /dev/null 2>&1; then
echo "β Inactive environment healthy"
else
echo "β Inactive environment failed health check"
exit 1
fi
# Switch traffic to new version
ln -sfn "$INACTIVE" "$CURRENT_LINK"
# Reload webserver
systemctl reload nginx
echo "β Switched to $INACTIVE_NAME environment"Learn about zero-downtime deployment strategies.
Database Migration Handling
Safely manage database schema changes during deployments.
Migration System
#!/bin/bash
# db-migrate.sh - Run database migrations
MIGRATIONS_DIR="/var/www/db/migrations"
APPLIED_FILE="/var/www/db/applied-migrations.txt"
touch "$APPLIED_FILE"
echo "Running database migrations..."
for MIGRATION in $(ls "$MIGRATIONS_DIR"/*.sql | sort); do
MIGRATION_NAME=$(basename "$MIGRATION")
# Check if already applied
if grep -q "$MIGRATION_NAME" "$APPLIED_FILE"; then
echo "β Skipping: $MIGRATION_NAME (already applied)"
continue
fi
echo "Applying: $MIGRATION_NAME"
# Run migration
if wp db query < "$MIGRATION"; then
echo "$MIGRATION_NAME" >> "$APPLIED_FILE"
echo "β Applied: $MIGRATION_NAME"
else
echo "β Failed: $MIGRATION_NAME"
exit 1
fi
done
echo "β All migrations applied"Reversible Migrations
# Migration file structure
# migrations/001-add-custom-table.sql (up)
# migrations/001-add-custom-table-rollback.sql (down)
#!/bin/bash
# rollback-migration.sh
MIGRATION_ID="$1"
MIGRATIONS_DIR="/var/www/db/migrations"
ROLLBACK_FILE="$MIGRATIONS_DIR/${MIGRATION_ID}-rollback.sql"
if [ ! -f "$ROLLBACK_FILE" ]; then
echo "β Rollback file not found: $ROLLBACK_FILE"
exit 1
fi
echo "Rolling back migration: $MIGRATION_ID"
wp db query < "$ROLLBACK_FILE"
echo "β Rollback complete"Deployment Notifications
Alert teams about deployment status.
Slack Notifications
#!/bin/bash
# notify-deployment.sh
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
STATUS="$1" # success or failure
COMMIT="$2"
DEPLOYER="$3"
if [ "$STATUS" = "success" ]; then
COLOR="good"
MESSAGE="Deployment successful"
else
COLOR="danger"
MESSAGE="Deployment failed"
fi
curl -X POST "$SLACK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{
\"attachments\": [{
\"color\": \"$COLOR\",
\"title\": \"$MESSAGE\",
\"fields\": [
{\"title\": \"Environment\", \"value\": \"Production\", \"short\": true},
{\"title\": \"Commit\", \"value\": \"$COMMIT\", \"short\": true},
{\"title\": \"Deployed by\", \"value\": \"$DEPLOYER\", \"short\": true}
]
}]
}"Email Notifications
#!/bin/bash
# email-deployment-report.sh
EMAIL="team@example.com"
STATUS="$1"
DETAILS="$2"
SUBJECT="WordPress Deployment $STATUS"
cat <<EOF | mail -s "$SUBJECT" "$EMAIL"
WordPress Deployment Report
===========================
Status: $STATUS
Time: $(date)
Server: $(hostname)
Deployed by: $(whoami)
Details:
$DETAILS
Automated deployment system
EOFComplete Production Pipeline
Integrate all components into a complete system.
Master Deployment Script
#!/bin/bash
# deploy.sh - Complete deployment pipeline
set -euo pipefail
ENVIRONMENT="${1:-staging}"
LOG_FILE="/var/log/deployments/deploy-$(date +%Y%m%d-%H%M%S).log"
mkdir -p "$(dirname $LOG_FILE)"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $@" | tee -a "$LOG_FILE"
}
notify() {
./notify-deployment.sh "$1" "$2" "$(whoami)"
}
log "=== Deployment Started ==="
log "Environment: $ENVIRONMENT"
# Pre-deployment checks
log "Running pre-deployment checks..."
./pre-deploy-check.sh "$ENVIRONMENT" || {
log "β Pre-deployment checks failed"
notify "failure" "Pre-deployment checks failed"
exit 1
}
# Backup
log "Creating backup..."
./backup.sh "$ENVIRONMENT"
# Deploy code
log "Deploying code..."
./deploy-code.sh "$ENVIRONMENT" || {
log "β Code deployment failed"
./rollback.sh "$ENVIRONMENT"
notify "failure" "Code deployment failed"
exit 1
}
# Database migrations
log "Running database migrations..."
./db-migrate.sh || {
log "β Database migration failed"
./rollback.sh "$ENVIRONMENT"
notify "failure" "Database migration failed"
exit 1
}
# Post-deployment validation
log "Validating deployment..."
./validate-deployment.sh "$ENVIRONMENT" || {
log "β Validation failed"
./rollback.sh "$ENVIRONMENT"
notify "failure" "Deployment validation failed"
exit 1
}
# Success
log "=== Deployment Complete ==="
notify "success" "Deployment to $ENVIRONMENT successful"
echo "β Deployment successful"
echo "Log: $LOG_FILE"Next Steps
You now have a complete WordPress deployment pipeline built with WP-CLI and Bash.
Recommended Learning Path
Week 1: Basic automation
- Create simple deploy scripts
- Add backup mechanisms
- Implement rollback procedures
Week 2: Testing integration
- Add validation checks
- Create health checks
- Automate testing
Week 3: Zero-downtime
- Implement symlink strategy
- Set up blue-green deployment
- Test switching mechanisms
Week 4: Production hardening
- Add monitoring
- Implement notifications
- Document procedures
Advanced Topics
- Container Deployments – Docker-based pipelines
- GitOps Workflows – Git-driven deployments
- Multi-Region Deployment – Global site deployment
Get More Resources
Download deployment scripts including:
- Complete pipeline system
- Testing frameworks
- Rollback automation
- Weekly WP-CLI tutorials
- DevOps best practices
- Deployment strategies
Conclusion
A WordPress deployment pipeline transforms risky manual deployments into reliable, automated workflows that enable rapid, confident releases to production.
What we covered:
β
Deployment pipeline components and stages
β
Basic deployment automation with backups
β
Staging to production workflows
β
Zero-downtime deployment strategies
β
Database migration handling
β
Complete production pipeline integration
Master these techniques, and youβll deploy WordPress changes confidently dozens of times per day with zero downtime and instant rollback capability.
Ready for more? Learn continuous integration or infrastructure as code.
Questions about WordPress deployment pipelines? Drop a comment below!
Found this helpful? Share with other WordPress DevOps teams.
