Build a WordPress Deployment Pipeline with WP-CLI and Bash

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 health

Essential 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.

#!/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
EOF

Complete 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.

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

  1. Container Deployments – Docker-based pipelines
  2. GitOps Workflows – Git-driven deployments
  3. Multi-Region Deployment – Global site deployment

Get More Resources

Download deployment scripts including:

  • Complete pipeline system
  • Testing frameworks
  • Rollback automation

Join our email course for:

  • 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.