<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>WordPress DevOps Archives - WP-CLI Mastery</title>
	<atom:link href="https://wpclimastery.com/blog/category/wordpress-devops/feed/" rel="self" type="application/rss+xml" />
	<link>https://wpclimastery.com/blog/category/wordpress-devops/</link>
	<description>Automate WordPress Like a DevOps Pro.</description>
	<lastBuildDate>Mon, 24 Nov 2025 11:16:47 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://wpclimastery.com/wp-content/uploads/2025/11/cropped-favicon-32x32.webp</url>
	<title>WordPress DevOps Archives - WP-CLI Mastery</title>
	<link>https://wpclimastery.com/blog/category/wordpress-devops/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>WordPress CI/CD Pipeline with GitHub Actions and WP-CLI</title>
		<link>https://wpclimastery.com/blog/wordpress-ci-cd-pipeline-with-github-actions-and-wp-cli/</link>
		
		<dc:creator><![CDATA[Krasen]]></dc:creator>
		<pubDate>Tue, 20 Jan 2026 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress DevOps]]></category>
		<category><![CDATA[continuous integration wordpress]]></category>
		<category><![CDATA[github actions wordpress]]></category>
		<category><![CDATA[wordpress ci cd]]></category>
		<category><![CDATA[wordpress deployment automation]]></category>
		<category><![CDATA[wp-cli devops]]></category>
		<guid isPermaLink="false">https://wpclimastery.com/?p=15</guid>

					<description><![CDATA[<p>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,...</p>
<p>The post <a href="https://wpclimastery.com/blog/wordpress-ci-cd-pipeline-with-github-actions-and-wp-cli/">WordPress CI/CD Pipeline with GitHub Actions and WP-CLI</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>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&#8217;s no easy rollback.</p>



<p>Professional WordPress development requires automated CI/CD (Continuous Integration/Continuous Deployment) pipelines that test, build, and deploy code safely and consistently.</p>



<p>In this advanced guide, you&#8217;ll learn to build a production-ready WordPress CI/CD pipeline using GitHub Actions and WP-CLI. You&#8217;ll implement automated testing, staging deployments, database migrations, and zero-downtime production deployments—all triggered automatically by Git pushes.</p>



<p>By the end, you&#8217;ll have a professional WordPress DevOps workflow that eliminates deployment fear.</p>



<h3 class="wp-block-heading" id="what-is-cicd-for-wordpress-what-is-cicd">What is CI/CD for WordPress?</h3>



<h4 class="wp-block-heading" id="cicd-defined">CI/CD Defined</h4>



<p><strong>Continuous Integration (CI):</strong></p>



<ul class="wp-block-list">
<li>Automatically test code changes</li>



<li>Verify WordPress functionality</li>



<li>Run security checks</li>



<li>Ensure code quality</li>
</ul>



<p><strong>Continuous Deployment (CD):</strong></p>



<ul class="wp-block-list">
<li>Automatically deploy to staging</li>



<li>Deploy to production on approval</li>



<li>Run database migrations</li>



<li>Zero-downtime deployments</li>
</ul>



<h4 class="wp-block-heading" id="the-manual-deployment-problem">The Manual Deployment Problem</h4>



<p><strong>Traditional workflow:</strong></p>



<ol class="wp-block-list">
<li>Make changes locally</li>



<li>FTP files to server (hope you don&#8217;t overwrite something)</li>



<li>SSH and run SQL updates manually</li>



<li>Clear caches</li>



<li>Hope nothing is broken</li>



<li>If broken, scramble to fix or revert</li>
</ol>



<p><strong>Problems:</strong></p>



<ul class="wp-block-list">
<li>Downtime during deployment</li>



<li>Human error (forgot to run migration, uploaded wrong file)</li>



<li>No testing before production</li>



<li>Difficult rollback</li>



<li>No audit trail</li>
</ul>



<h4 class="wp-block-heading" id="cicd-workflow">CI/CD Workflow</h4>



<p><strong>Automated workflow:</strong></p>



<ol class="wp-block-list">
<li>Push code to GitHub</li>



<li><strong>CI</strong>: Automated tests run</li>



<li><strong>CD</strong>: Deploy to staging automatically</li>



<li>QA tests staging environment</li>



<li>Approve production deployment</li>



<li><strong>CD</strong>: Deploy to production with zero downtime</li>



<li>Automatic rollback if issues detected</li>
</ol>



<p><strong>Benefits:</strong></p>



<ul class="wp-block-list">
<li>No manual deployment steps</li>



<li>Every deployment is tested</li>



<li>Instant rollback capability</li>



<li>Complete audit trail in Git</li>



<li>Confidence to deploy frequently</li>
</ul>



<h3 class="wp-block-heading" id="prerequisites-prerequisites">Prerequisites</h3>



<p>Before building your CI/CD pipeline:</p>



<h4 class="wp-block-heading" id="required-accounts">Required Accounts</h4>



<ul class="wp-block-list">
<li><strong>GitHub Account</strong>&nbsp;&#8211; For repository and Actions</li>



<li><strong>WordPress Server(s)</strong>&nbsp;&#8211; Staging and production with SSH access</li>



<li><strong>WP-CLI Access</strong>&nbsp;&#8211; On all WordPress servers</li>
</ul>



<h4 class="wp-block-heading" id="server-requirements">Server Requirements</h4>



<p><strong>Both staging and production need:</strong></p>



<ul class="wp-block-list">
<li>SSH access</li>



<li>WP-CLI installed &#8211; <a href="#">Installation guide</a></li>



<li>Git installed</li>



<li>Web server (Apache/Nginx)</li>



<li>MySQL/MariaDB database</li>
</ul>



<h4 class="wp-block-heading" id="local-development">Local Development</h4>



<ul class="wp-block-list">
<li>Git installed</li>



<li>Basic understanding of WordPress development</li>



<li><a href="#">Bash scripting knowledge</a></li>
</ul>



<h4 class="wp-block-heading" id="knowledge-requirements">Knowledge Requirements</h4>



<p>Helpful background:</p>



<ul class="wp-block-list">
<li>Git basics (branches, commits, pull requests)</li>



<li>GitHub fundamentals</li>



<li>WordPress theme/plugin development</li>



<li>SSH and server administration</li>
</ul>



<h3 class="wp-block-heading" id="github-actions-basics-github-actions-basics">GitHub Actions Basics</h3>



<h4 class="wp-block-heading" id="what-are-github-actions">What are GitHub Actions?</h4>



<p>GitHub Actions are automated workflows that run when specific GitHub events occur (push, pull request, release, etc.).</p>



<p><strong>Workflow anatomy:</strong></p>



<pre class="wp-block-code"><code>name: WordPress CI/CD

on:
  push:
    branches: &#91;main, develop]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run tests
        run: ./run-tests.sh
</code></pre>



<p><strong>Key concepts:</strong></p>



<ul class="wp-block-list">
<li><strong>Workflow</strong>: YAML file defining automation</li>



<li><strong>Event</strong>: Trigger (push, pull_request, schedule)</li>



<li><strong>Job</strong>: Set of steps that run on a runner</li>



<li><strong>Step</strong>: Individual task (run command, use action)</li>



<li><strong>Runner</strong>: Server that runs workflows (GitHub-hosted or self-hosted)</li>
</ul>



<h4 class="wp-block-heading" id="why-github-actions-for-wordpress">Why GitHub Actions for WordPress?</h4>



<p><strong>Advantages:</strong></p>



<ul class="wp-block-list">
<li>Free for public repos (2,000 minutes/month for private)</li>



<li>Integrated with GitHub (no external CI service)</li>



<li>Large marketplace of pre-built actions</li>



<li>Supports matrix builds (test multiple PHP versions)</li>



<li>Secret management built-in</li>
</ul>



<p>Learn more:&nbsp;<a href="https://docs.github.com/en/actions">GitHub Actions Documentation</a></p>



<h3 class="wp-block-heading" id="setting-up-your-repository-repository-setup">Setting Up Your Repository</h3>



<h4 class="wp-block-heading" id="repository-structure">Repository Structure</h4>



<p>Organize your WordPress project for CI/CD:</p>



<pre class="wp-block-code"><code>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
</code></pre>



<p><strong>What to version control:</strong></p>



<ul class="wp-block-list">
<li>✅ Custom themes</li>



<li>✅ Custom plugins</li>



<li>✅ mu-plugins</li>



<li>✅ Configuration files</li>



<li>✅ Deployment scripts</li>
</ul>



<p><strong>What NOT to version:</strong></p>



<ul class="wp-block-list">
<li>❌ WordPress core (use Composer or WP-CLI)</li>



<li>❌ Third-party plugins (manage separately)</li>



<li>❌&nbsp;<code>wp-config.php</code>&nbsp;(use environment-specific configs)</li>



<li>❌&nbsp;<code>uploads/</code>&nbsp;directory</li>



<li>❌&nbsp;<code>.env</code>&nbsp;files with secrets</li>
</ul>



<h4 class="wp-block-heading" id="create-gitignore">Create .gitignore</h4>



<pre class="wp-block-code"><code><em># .gitignore for WordPress</em>

<em># WordPress core</em>
/wp-admin/
/wp-includes/
/wp-*.php
/xmlrpc.php
/license.txt
/readme.html

<em># Configuration</em>
wp-config.php
.env
.env.*

<em># Uploads</em>
wp-content/uploads/

<em># Cache</em>
wp-content/cache/
wp-content/backup*/

<em># Third-party plugins (manage via Composer)</em>
wp-content/plugins/*
!wp-content/plugins/my-custom-plugin/

<em># IDE</em>
.vscode/
.idea/

<em># OS</em>
.DS_Store
Thumbs.db

<em># Dependencies</em>
/vendor/
node_modules/
</code></pre>



<h4 class="wp-block-heading" id="initialize-repository">Initialize Repository</h4>



<pre class="wp-block-code"><code><em># Create repository</em>
git init
git add .
git commit -m "Initial commit"

<em># Create and push to GitHub</em>
gh repo create my-wordpress-site --public --source=. --remote=origin --push

<em># Or manually</em>
git remote add origin git@github.com:username/my-wordpress-site.git
git push -u origin main
</code></pre>



<h3 class="wp-block-heading" id="wordpress-testing-environment-testing-environment">WordPress Testing Environment</h3>



<h4 class="wp-block-heading" id="set-up-wordpress-in-github-actions">Set Up WordPress in GitHub Actions</h4>



<p>Use the&nbsp;<code>setup-wordpress</code>&nbsp;action to create a test environment:</p>



<pre class="wp-block-code"><code><em># .github/workflows/ci.yml</em>

name: WordPress CI

on:
  push:
    branches: &#91;main, develop]
  pull_request:
    branches: &#91;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
</code></pre>



<h4 class="wp-block-heading" id="create-test-script">Create Test Script</h4>



<pre class="wp-block-code"><code>#!/bin/bash
<em># scripts/test.sh</em>

set -e

WP_PATH="/tmp/wordpress"

echo "Running WordPress tests..."

<em># Test 1: WordPress is installed</em>
wp core is-installed --path=$WP_PATH

<em># Test 2: Database is accessible</em>
wp db check --path=$WP_PATH

<em># Test 3: Plugin is active</em>
wp plugin is-active my-plugin --path=$WP_PATH

<em># Test 4: WordPress version</em>
version=$(wp core version --path=$WP_PATH)
echo "WordPress version: $version"

<em># Test 5: Run PHP CodeSniffer (if configured)</em>
if command -v phpcs &amp;&gt; /dev/null; then
    phpcs --standard=WordPress wp-content/plugins/my-plugin/
fi

echo "✓ All tests passed!"
</code></pre>



<p>Make it executable:</p>



<pre class="wp-block-code"><code>chmod +x scripts/test.sh
git add scripts/test.sh
git commit -m "Add test script"
</code></pre>



<h3 class="wp-block-heading" id="automated-testing-with-wp-cli-automated-testing">Automated Testing with WP-CLI</h3>



<h4 class="wp-block-heading" id="test-categories">Test Categories</h4>



<p><strong>1. Sanity Tests</strong></p>



<ul class="wp-block-list">
<li>WordPress is installed</li>



<li>Database connection works</li>



<li>Required plugins are active</li>
</ul>



<p><strong>2. Functionality Tests</strong></p>



<ul class="wp-block-list">
<li>Custom plugin functions work</li>



<li>Theme renders correctly</li>



<li>Custom post types exist</li>
</ul>



<p><strong>3. Security Tests</strong></p>



<ul class="wp-block-list">
<li>No vulnerable plugins</li>



<li>WordPress core integrity</li>



<li>File permissions</li>
</ul>



<p><strong>4. Performance Tests</strong></p>



<ul class="wp-block-list">
<li>Database query count</li>



<li>Page load time</li>



<li>Cache functionality</li>
</ul>



<h4 class="wp-block-heading" id="comprehensive-test-script">Comprehensive Test Script</h4>



<pre class="wp-block-code"><code>#!/bin/bash
<em># scripts/test.sh - Comprehensive WordPress testing</em>

set -e

WP_PATH=${WP_PATH:-/tmp/wordpress}
RED='\033&#91;0;31m'
GREEN='\033&#91;0;32m'
NC='\033&#91;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" &amp;&gt;/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 "========================================="

<em># Sanity tests</em>
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"

<em># Plugin tests</em>
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"

<em># Theme tests</em>
run_test "Theme activated" "wp theme is-active my-theme --path=$WP_PATH"

<em># Security tests</em>
run_test "No vulnerable plugins" "! wp plugin list --path=$WP_PATH --field=update | grep -q available"

<em># Performance tests</em>
run_test "Cache object working" "wp cache flush --path=$WP_PATH"

<em># Database tests</em>
run_test "Database optimized" "wp db optimize --path=$WP_PATH"

echo "========================================="
echo "Test Results: $passed passed, $failed failed"
echo "========================================="

if &#91; $failed -gt 0 ]; then
    exit 1
fi

exit 0
</code></pre>



<h4 class="wp-block-heading" id="matrix-testing-multiple-php-versions">Matrix Testing (Multiple PHP Versions)</h4>



<pre class="wp-block-code"><code>jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        php-version: &#91;'7.4', '8.0', '8.1']
        wordpress-version: &#91;'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
</code></pre>



<p>This tests across 9 combinations (3 PHP × 3 WP versions).</p>



<h3 class="wp-block-heading" id="building-the-deployment-workflow-deployment-workflow">Building the Deployment Workflow</h3>



<h4 class="wp-block-heading" id="deployment-script-structure">Deployment Script Structure</h4>



<pre class="wp-block-code"><code>#!/bin/bash
<em># scripts/deploy.sh</em>

set -euo pipefail

ENVIRONMENT=$1  <em># staging or production</em>
SERVER=$2       <em># server hostname</em>
WP_PATH=$3      <em># WordPress path on server</em>

echo "Deploying to $ENVIRONMENT ($SERVER)"

<em># 1. Create backup</em>
ssh $SERVER "cd $WP_PATH &amp;&amp; wp db export backup-$(date +%s).sql"

<em># 2. Pull latest code</em>
ssh $SERVER "cd $WP_PATH &amp;&amp; git pull origin main"

<em># 3. Install dependencies</em>
ssh $SERVER "cd $WP_PATH &amp;&amp; composer install --no-dev"

<em># 4. Run database migrations</em>
ssh $SERVER "cd $WP_PATH &amp;&amp; wp cli migrate"

<em># 5. Clear caches</em>
ssh $SERVER "cd $WP_PATH &amp;&amp; wp cache flush"
ssh $SERVER "cd $WP_PATH &amp;&amp; wp rewrite flush"

<em># 6. Verify deployment</em>
ssh $SERVER "cd $WP_PATH &amp;&amp; wp core is-installed"

echo "✓ Deployment complete!"
</code></pre>



<h4 class="wp-block-heading" id="ssh-key-setup">SSH Key Setup</h4>



<p>Add SSH private key to GitHub Secrets:</p>



<ol class="wp-block-list">
<li>Generate SSH key (if needed):</li>
</ol>



<pre class="wp-block-code"><code>ssh-keygen -t ed25519 -C "github-actions@example.com" -f ~/.ssh/github-actions
</code></pre>



<ol start="2" class="wp-block-list">
<li>Add public key to server:</li>
</ol>



<pre class="wp-block-code"><code>ssh-copy-id -i ~/.ssh/github-actions.pub user@your-server.com
</code></pre>



<ol start="3" class="wp-block-list">
<li>Add private key to GitHub:
<ul class="wp-block-list">
<li>Go to repo Settings → Secrets → Actions</li>



<li>New repository secret</li>



<li>Name:&nbsp;<code>SSH_PRIVATE_KEY</code></li>



<li>Value: Contents of&nbsp;<code>~/.ssh/github-actions</code>&nbsp;(private key)</li>
</ul>
</li>
</ol>



<h3 class="wp-block-heading" id="environment-secrets-management-secrets-management">Environment Secrets Management</h3>



<h4 class="wp-block-heading" id="github-secrets">GitHub Secrets</h4>



<p>Store sensitive data in GitHub Secrets:</p>



<p><strong>Required secrets:</strong></p>



<ul class="wp-block-list">
<li><code>SSH_PRIVATE_KEY</code>&nbsp;&#8211; SSH key for server access</li>



<li><code>SSH_HOST_STAGING</code>&nbsp;&#8211; Staging server hostname</li>



<li><code>SSH_HOST_PRODUCTION</code>&nbsp;&#8211; Production server hostname</li>



<li><code>SSH_USER</code>&nbsp;&#8211; SSH username</li>



<li><code>WP_PATH_STAGING</code>&nbsp;&#8211; WordPress path on staging</li>



<li><code>WP_PATH_PRODUCTION</code>&nbsp;&#8211; WordPress path on production</li>
</ul>



<h4 class="wp-block-heading" id="using-secrets-in-workflows">Using Secrets in Workflows</h4>



<pre class="wp-block-code"><code>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 }}
</code></pre>



<h4 class="wp-block-heading" id="environment-variables">Environment Variables</h4>



<p>Create environment-specific configurations:</p>



<pre class="wp-block-code"><code>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 }}
</code></pre>



<h3 class="wp-block-heading" id="staging-deployment-staging-deployment">Staging Deployment</h3>



<h4 class="wp-block-heading" id="auto-deploy-to-staging">Auto-Deploy to Staging</h4>



<p>Deploy automatically on push to&nbsp;<code>develop</code>&nbsp;branch:</p>



<pre class="wp-block-code"><code><em># .github/workflows/deploy-staging.yml</em>

name: Deploy to Staging

on:
  push:
    branches: &#91;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 }} &lt;&lt; 'ENDSSH'
            set -e
            cd ${{ secrets.WP_PATH_STAGING }}

            <em># Backup database</em>
            wp db export backup-$(date +%Y%m%d-%H%M%S).sql

            <em># Pull latest code</em>
            git fetch origin
            git reset --hard origin/develop

            <em># Install dependencies</em>
            composer install --no-dev --optimize-autoloader

            <em># Database migrations</em>
            if &#91; -f scripts/migrate.sh ]; then
              ./scripts/migrate.sh
            fi

            <em># Clear caches</em>
            wp cache flush
            wp rewrite flush

            <em># Verify</em>
            wp core is-installed &amp;&amp; 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 }}
</code></pre>



<h3 class="wp-block-heading" id="production-deployment-production-deployment">Production Deployment</h3>



<h4 class="wp-block-heading" id="manual-approval-required">Manual Approval Required</h4>



<p>Require manual approval before production deployment:</p>



<pre class="wp-block-code"><code><em># .github/workflows/deploy-production.yml</em>

name: Deploy to Production

on:
  workflow_dispatch:  <em># Manual trigger only</em>
  release:
    types: &#91;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 }} &amp;&amp; wp db export backups/pre-deploy-$(date +%s).sql"

      - name: Deploy to production
        run: |
          ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST_PRODUCTION }} &lt;&lt; 'ENDSSH'
            set -e
            cd ${{ secrets.WP_PATH_PRODUCTION }}

            <em># Enable maintenance mode</em>
            wp maintenance-mode activate

            <em># Pull latest code</em>
            git fetch origin
            git reset --hard origin/main

            <em># Install dependencies</em>
            composer install --no-dev --optimize-autoloader

            <em># Database migrations</em>
            if &#91; -f scripts/migrate.sh ]; then
              ./scripts/migrate.sh
            fi

            <em># Clear caches</em>
            wp cache flush
            wp rewrite flush
            wp transient delete --all

            <em># Disable maintenance mode</em>
            wp maintenance-mode deactivate

            <em># Verify</em>
            wp core is-installed &amp;&amp; echo "Production deployment successful!"
          ENDSSH

      - name: Smoke tests
        run: |
          # Test homepage
          curl -f https://yoursite.com

          <em># Test critical pages</em>
          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 }} &amp;&amp; git reset --hard HEAD~1"
</code></pre>



<h4 class="wp-block-heading" id="protected-branches">Protected Branches</h4>



<p>Configure branch protection in GitHub:</p>



<ol class="wp-block-list">
<li>Go to Settings → Branches</li>



<li>Add rule for&nbsp;<code>main</code>&nbsp;branch</li>



<li>Enable:
<ul class="wp-block-list">
<li>Require pull request reviews (1+ approvals)</li>



<li>Require status checks to pass (CI tests)</li>



<li>Require branches to be up to date</li>
</ul>
</li>
</ol>



<p>This ensures production only receives tested, reviewed code.</p>



<h3 class="wp-block-heading" id="database-migrations-database-migrations">Database Migrations</h3>



<h4 class="wp-block-heading" id="migration-script-pattern">Migration Script Pattern</h4>



<pre class="wp-block-code"><code>#!/bin/bash
<em># scripts/migrate.sh</em>

set -e

WP_PATH=${WP_PATH:-.}

echo "Running database migrations..."

<em># Migration 001: Add custom table</em>
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

<em># Migration 002: Update post meta</em>
wp post meta update-all custom_field new_value --from=old_value --path=$WP_PATH

<em># Migration 003: Create custom taxonomy</em>
wp taxonomy register custom_taxonomy --path=$WP_PATH

echo "✓ Migrations complete!"
</code></pre>



<h4 class="wp-block-heading" id="version-controlled-migrations">Version-Controlled Migrations</h4>



<p>Use migration files:</p>



<pre class="wp-block-code"><code><em># migrations/001-create-custom-table.sql</em>
CREATE TABLE IF NOT EXISTS wp_custom_data (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  data longtext NOT NULL,
  PRIMARY KEY (id)
);

<em># migrations/002-add-meta-field.sh</em>
<em>#!/bin/bash</em>
wp post list --format=ids | xargs -I {} wp post meta update {} new_field default_value
</code></pre>



<p><strong>Migration runner:</strong></p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># scripts/migrate.sh</em>

WP_PATH=${WP_PATH:-.}
MIGRATIONS_DIR="migrations"

for migration in $MIGRATIONS_DIR/*; do
    echo "Running migration: $(basename $migration)"

    if &#91;&#91; $migration == *.sql ]]; then
        wp db query &lt; $migration --path=$WP_PATH
    elif &#91;&#91; $migration == *.sh ]]; then
        bash $migration
    fi

    echo "✓ Completed: $(basename $migration)"
done
</code></pre>



<h3 class="wp-block-heading" id="rollback-strategy-rollback">Rollback Strategy</h3>



<h4 class="wp-block-heading" id="automated-rollback-on-failure">Automated Rollback on Failure</h4>



<pre class="wp-block-code"><code>- name: Deploy with rollback
  id: deploy
  run: |
    # Get current commit for rollback
    CURRENT_COMMIT=$(ssh $SERVER "cd $WP_PATH &amp;&amp; git rev-parse HEAD")
    echo "current_commit=$CURRENT_COMMIT" &gt;&gt; $GITHUB_OUTPUT

    <em># Deploy</em>
    ./scripts/deploy.sh

- name: Verify deployment
  id: verify
  run: |
    # Run smoke tests
    curl -f https://yoursite.com || exit 1
    ssh $SERVER "cd $WP_PATH &amp;&amp; wp core is-installed" || exit 1

- name: Rollback on failure
  if: failure() &amp;&amp; steps.deploy.outcome == 'success'
  run: |
    echo "Deployment verification failed, rolling back..."

    <em># Restore database backup</em>
    BACKUP_FILE=$(ssh $SERVER "ls -t $WP_PATH/backups/*.sql | head -1")
    ssh $SERVER "cd $WP_PATH &amp;&amp; wp db import $BACKUP_FILE"

    <em># Restore code</em>
    ssh $SERVER "cd $WP_PATH &amp;&amp; git reset --hard ${{ steps.deploy.outputs.current_commit }}"

    <em># Clear caches</em>
    ssh $SERVER "cd $WP_PATH &amp;&amp; wp cache flush"

    echo "✓ Rollback complete"
    exit 1
</code></pre>



<h4 class="wp-block-heading" id="manual-rollback-workflow">Manual Rollback Workflow</h4>



<pre class="wp-block-code"><code><em># .github/workflows/rollback.yml</em>

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 }} &amp;&amp; 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/"
</code></pre>



<h3 class="wp-block-heading" id="complete-github-actions-workflow-complete-workflow">Complete GitHub Actions Workflow</h3>



<h4 class="wp-block-heading" id="production-ready-cicd">Production-Ready CI/CD</h4>



<pre class="wp-block-code"><code><em># .github/workflows/wordpress-cicd.yml</em>

name: WordPress CI/CD

on:
  push:
    branches: &#91;main, develop]
  pull_request:
    branches: &#91;main]
  workflow_dispatch:

env:
  PHP_VERSION: '8.1'

jobs:
  <em># Job 1: Run tests</em>
  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

  <em># Job 2: Deploy to staging</em>
  deploy-staging:
    name: Deploy to Staging
    needs: test
    if: github.ref == 'refs/heads/develop' &amp;&amp; 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 }}

  <em># Job 3: Deploy to production</em>
  deploy-production:
    name: Deploy to Production
    needs: test
    if: github.ref == 'refs/heads/main' &amp;&amp; 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 }}
</code></pre>



<h2 class="wp-block-heading" id="best-practices-best-practices">Best Practices</h2>



<h4 class="wp-block-heading" id="security">Security</h4>



<ol class="wp-block-list">
<li><strong>Never commit secrets</strong>&nbsp;&#8211; Use GitHub Secrets</li>



<li><strong>Restrict SSH access</strong>&nbsp;&#8211; Use dedicated deploy keys</li>



<li><strong>Audit deployments</strong>&nbsp;&#8211; Enable GitHub audit log</li>



<li><strong>Scan for vulnerabilities</strong>&nbsp;&#8211; Use GitHub&#8217;s Dependabot</li>
</ol>



<h4 class="wp-block-heading" id="deployment">Deployment</h4>



<ol class="wp-block-list">
<li><strong>Always test first</strong>&nbsp;&#8211; Run CI tests before deploy</li>



<li><strong>Use staging</strong>&nbsp;&#8211; Test on staging before production</li>



<li><strong>Backup before deploy</strong>&nbsp;&#8211; Automatic database backups</li>



<li><strong>Enable maintenance mode</strong>&nbsp;&#8211; During production deployments</li>



<li><strong>Monitor after deploy</strong>&nbsp;&#8211; Check logs and metrics</li>
</ol>



<h4 class="wp-block-heading" id="code-quality">Code Quality</h4>



<ol class="wp-block-list">
<li><strong>Use linters</strong>&nbsp;&#8211; PHP CodeSniffer, ESLint</li>



<li><strong>Run static analysis</strong>&nbsp;&#8211; PHPStan, Psalm</li>



<li><strong>Code coverage</strong>&nbsp;&#8211; Track test coverage</li>



<li><strong>Peer review</strong>&nbsp;&#8211; Require PR approvals</li>
</ol>



<h4 class="wp-block-heading" id="performance">Performance</h4>



<ol class="wp-block-list">
<li><strong>Cache workflows</strong>&nbsp;&#8211; Cache Composer dependencies</li>



<li><strong>Parallel jobs</strong>&nbsp;&#8211; Run tests in parallel</li>



<li><strong>Self-hosted runners</strong>&nbsp;&#8211; For faster deploys</li>
</ol>



<h3 class="wp-block-heading" id="troubleshooting-troubleshooting">Troubleshooting</h3>



<h4 class="wp-block-heading" id="issue-1-ssh-connection-failed">Issue 1: SSH Connection Failed</h4>



<p><strong>Problem</strong>:&nbsp;<code>Permission denied (publickey)</code></p>



<p><strong>Solutions</strong>:</p>



<ol class="wp-block-list">
<li>Verify SSH key is added to GitHub Secrets correctly</li>



<li>Test SSH connection:</li>
</ol>



<pre class="wp-block-code"><code>ssh -i ~/.ssh/github-actions user@server.com
</code></pre>



<ol start="3" class="wp-block-list">
<li>Check server&#8217;s&nbsp;<code>~/.ssh/authorized_keys</code></li>



<li>Verify SSH agent setup in workflow</li>
</ol>



<h4 class="wp-block-heading" id="issue-2-deployment-timeout">Issue 2: Deployment Timeout</h4>



<p><strong>Problem</strong>: Workflow times out during deployment</p>



<p><strong>Solutions</strong>:</p>



<ol class="wp-block-list">
<li>Increase timeout:</li>
</ol>



<pre class="wp-block-code"><code>jobs:
  deploy:
    timeout-minutes: 30  <em># Default is 360</em>
</code></pre>



<ol start="2" class="wp-block-list">
<li>Optimize deployment script</li>



<li>Use caching for dependencies</li>
</ol>



<h4 class="wp-block-heading" id="issue-3-database-migration-failed">Issue 3: Database Migration Failed</h4>



<p><strong>Problem</strong>: Migration fails, breaks site</p>



<p><strong>Solutions</strong>:</p>



<ol class="wp-block-list">
<li>Always backup before migrations</li>



<li>Test migrations on staging first</li>



<li>Use transactions when possible:</li>
</ol>



<pre class="wp-block-code"><code>START TRANSACTION;
<em>-- migration SQL</em>
COMMIT;
</code></pre>



<ol start="4" class="wp-block-list">
<li>Implement rollback in migration script</li>
</ol>



<h4 class="wp-block-heading" id="issue-4-cache-not-cleared">Issue 4: Cache Not Cleared</h4>



<p><strong>Problem</strong>: Changes don&#8217;t appear after deployment</p>



<p><strong>Solutions</strong>:</p>



<ol class="wp-block-list">
<li>Add cache clearing to deployment:</li>
</ol>



<pre class="wp-block-code"><code>wp cache flush
wp rewrite flush
wp transient delete --all
</code></pre>



<ol start="2" class="wp-block-list">
<li>Clear CDN cache if using one</li>



<li>Verify opcache is reset</li>
</ol>



<h4 class="wp-block-heading" id="issue-5-workflow-fails-silently">Issue 5: Workflow Fails Silently</h4>



<p><strong>Problem</strong>: Workflow completes but deployment didn&#8217;t work</p>



<p><strong>Solutions</strong>:</p>



<ol class="wp-block-list">
<li>Remove&nbsp;<code>set -e</code>&nbsp;temporarily to see errors</li>



<li>Add verbose logging:</li>
</ol>



<pre class="wp-block-code"><code>set -x  <em># Print commands</em>
</code></pre>



<ol start="3" class="wp-block-list">
<li>Check server logs:</li>
</ol>



<pre class="wp-block-code"><code>ssh server "tail -f /var/log/apache2/error.log"
</code></pre>



<h3 class="wp-block-heading" id="next-steps-next-steps">Next Steps</h3>



<p>Congratulations! You&#8217;ve built a production-ready WordPress CI/CD pipeline.</p>



<h4 class="wp-block-heading" id="enhance-your-pipeline">Enhance Your Pipeline</h4>



<ol class="wp-block-list">
<li><strong><a href="https://file+.vscode-resource.vscode-cdn.net/blog/multisite-bulk-operations-wpcli">WordPress Multisite CI/CD</a></strong>&nbsp;&#8211; Deploy to network</li>



<li><strong><a href="https://file+.vscode-resource.vscode-cdn.net/blog/automate-wordpress-backups-wpcli">Automated Backups</a></strong>&nbsp;&#8211; Integrate with deployment</li>



<li><strong><a href="https://file+.vscode-resource.vscode-cdn.net/blog/optimize-wordpress-performance-wpcli">Performance Testing</a></strong>&nbsp;&#8211; Add to CI</li>
</ol>



<h4 class="wp-block-heading" id="related-guides">Related Guides</h4>



<ul class="wp-block-list">
<li><strong><a href="#">Custom WP-CLI Commands</a></strong> &#8211; Build deployment commands</li>



<li><strong><a href="#">Environment Management</a></strong> &#8211; Advanced configs</li>



<li><strong><a href="#">Bash Functions</a></strong> &#8211; Improve scripts</li>
</ul>



<h4 class="wp-block-heading" id="advanced-topics">Advanced Topics</h4>



<p><strong>Add to your pipeline:</strong></p>



<ul class="wp-block-list">
<li>Visual regression testing (BackstopJS, Percy)</li>



<li>Load testing (k6, Apache JBench)</li>



<li>Security scanning (OWASP ZAP)</li>



<li>Accessibility testing (pa11y)</li>



<li>SEO validation</li>



<li>Automated changelogs</li>
</ul>



<h4 class="wp-block-heading" id="master-wordpress-devops">Master WordPress DevOps</h4>



<p>Want to build enterprise-grade WordPress automation?</p>



<p><strong><a href="/#get-started">Join WPCLI Mastery</a></strong> and learn:</p>



<ul class="wp-block-list">
<li>Complete CI/CD pipeline templates</li>



<li>Multi-environment WordPress architecture</li>



<li>Blue-green deployments</li>



<li>Automated rollbacks and disaster recovery</li>



<li>Early bird course pricing ($99 vs $199)</li>
</ul>



<h2 class="wp-block-heading" id="conclusion">Conclusion</h2>



<p>Automated CI/CD pipelines eliminate deployment fear and enable rapid, confident WordPress development. The GitHub Actions workflow you built today provides:</p>



<ul class="wp-block-list">
<li>Automated testing on every push</li>



<li>Safe staging deployments</li>



<li>Controlled production releases</li>



<li>Instant rollback capability</li>



<li>Complete audit trail</li>
</ul>



<p><strong>Key takeaways:</strong></p>



<ul class="wp-block-list">
<li>CI/CD catches errors before production</li>



<li>GitHub Actions integrates seamlessly with WordPress</li>



<li>WP-CLI makes WordPress automation possible</li>



<li>Staging → Production workflow ensures safety</li>



<li>Automated rollbacks prevent disasters</li>



<li>Database migrations need careful handling</li>
</ul>



<p>The CI/CD pipeline you implemented today is the foundation of professional WordPress DevOps. Deploy with confidence.</p>



<p><strong>Ready to implement?</strong>&nbsp;Start with the complete workflow and adapt to your needs.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p><strong>Questions about WordPress CI/CD?</strong>&nbsp;Drop a comment below!</p>



<p><strong>Found this helpful?</strong>&nbsp;Share with WordPress developers building automation.</p>



<p><strong>Next:</strong> Explore <a href="#">advanced WordPress deployment strategies</a> and zero-downtime techniques.</p>
<p>The post <a href="https://wpclimastery.com/blog/wordpress-ci-cd-pipeline-with-github-actions-and-wp-cli/">WordPress CI/CD Pipeline with GitHub Actions and WP-CLI</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>WordPress Development with Docker and WP-CLI: Complete Setup Guide</title>
		<link>https://wpclimastery.com/blog/wordpress-development-with-docker-and-wp-cli-complete-setup-guide/</link>
		
		<dc:creator><![CDATA[Krasen]]></dc:creator>
		<pubDate>Thu, 25 Dec 2025 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress DevOps]]></category>
		<category><![CDATA[containerized wordpress]]></category>
		<category><![CDATA[docker compose wordpress]]></category>
		<category><![CDATA[docker wordpress]]></category>
		<category><![CDATA[wordpress development environment]]></category>
		<category><![CDATA[wp-cli docker]]></category>
		<guid isPermaLink="false">https://wpclimastery.com/?p=140</guid>

					<description><![CDATA[<p>Setting up local WordPress environments manually wastes hours—installing PHP, MySQL, configuring virtual hosts, managing multiple versions. Every project needs different configurations, and your local machine becomes cluttered with conflicting dependencies....</p>
<p>The post <a href="https://wpclimastery.com/blog/wordpress-development-with-docker-and-wp-cli-complete-setup-guide/">WordPress Development with Docker and WP-CLI: Complete Setup Guide</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Setting up local WordPress environments manually wastes hours—installing PHP, MySQL, configuring virtual hosts, managing multiple versions. Every project needs different configurations, and your local machine becomes cluttered with conflicting dependencies.</p>



<p>Docker + WP-CLI creates isolated, reproducible WordPress environments in minutes. Spin up fresh WordPress installs with one command, test on multiple PHP versions simultaneously, and share identical environments with your entire team.</p>



<p>In this guide, you’ll learn to build production-ready WordPress development environments using Docker and WP-CLI, with automated setup scripts and best practices used by modern WordPress development teams.</p>



<h3 class="wp-block-heading" id="why-docker">Why Docker for WordPress Development?</h3>



<p><a href="https://www.docker.com/">Docker containers</a> provide isolated, consistent environments for WordPress development without polluting your local system.</p>



<h4 class="wp-block-heading" id="problems-with-traditional-local-wordpress">Problems with Traditional Local WordPress</h4>



<p><strong>Version conflicts</strong>: Multiple PHP versions cause headaches and break projects.</p>



<p><strong>Configuration drift</strong>: Development doesn’t match staging or production environments.</p>



<p><strong>Slow setup</strong>: Installing WordPress manually on each project wastes 30-60 minutes.</p>



<p><strong>Team inconsistency</strong>: Developers have different local setups causing “works on my machine” bugs.</p>



<p><strong>Resource waste</strong>: Running multiple databases and servers consumes system resources.</p>



<h4 class="wp-block-heading" id="docker-wp-cli-advantages">Docker + WP-CLI Advantages</h4>



<p><strong>Isolated environments</strong>: Each project runs independently without conflicts.</p>



<p><strong>Instant setup</strong>: Launch complete WordPress stack in under 2 minutes.</p>



<p><strong>Version flexibility</strong>: Test on PHP 7.4, 8.0, 8.1 simultaneously.</p>



<p><strong>Team consistency</strong>: Everyone runs identical environments via docker-compose.yml.</p>



<p><strong>Easy cleanup</strong>: Delete containers without leaving system residue.</p>



<p>According to <a href="https://www.docker.com/">Docker adoption surveys</a>, 65% of professional WordPress developers use Docker for local development.</p>



<h3 class="wp-block-heading" id="docker-setup">Docker WordPress Setup</h3>



<p>Create a complete WordPress development environment with Docker Compose.</p>



<h4 class="wp-block-heading" id="basic-docker-compose.yml">Basic docker-compose.yml</h4>



<div class="sourceCode" id="cb1">
<pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="fu">version</span><span class="kw">:</span><span class="at"> </span><span class="st">"3.8"</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a><span class="fu">services</span><span class="kw">:</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="at">  </span><span class="fu">wordpress</span><span class="kw">:</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a><span class="at">    </span><span class="fu">image</span><span class="kw">:</span><span class="at"> wordpress:latest</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a><span class="at">    </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="st">"8080:80"</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a><span class="at">    </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_HOST</span><span class="kw">:</span><span class="at"> db</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_USER</span><span class="kw">:</span><span class="at"> wordpress</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_PASSWORD</span><span class="kw">:</span><span class="at"> wordpress</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_NAME</span><span class="kw">:</span><span class="at"> wordpress</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true"></a><span class="at">    </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> ./wordpress:/var/www/html</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true"></a><span class="at">    </span><span class="fu">depends_on</span><span class="kw">:</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> db</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true"></a></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true"></a><span class="at">  </span><span class="fu">db</span><span class="kw">:</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true"></a><span class="at">    </span><span class="fu">image</span><span class="kw">:</span><span class="at"> mysql:8.0</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true"></a><span class="at">    </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true"></a><span class="at">      </span><span class="fu">MYSQL_DATABASE</span><span class="kw">:</span><span class="at"> wordpress</span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true"></a><span class="at">      </span><span class="fu">MYSQL_USER</span><span class="kw">:</span><span class="at"> wordpress</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true"></a><span class="at">      </span><span class="fu">MYSQL_PASSWORD</span><span class="kw">:</span><span class="at"> wordpress</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true"></a><span class="at">      </span><span class="fu">MYSQL_ROOT_PASSWORD</span><span class="kw">:</span><span class="at"> rootpassword</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true"></a><span class="at">    </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> db_data:/var/lib/mysql</span></span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true"></a></span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true"></a><span class="at">  </span><span class="fu">wpcli</span><span class="kw">:</span></span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true"></a><span class="at">    </span><span class="fu">image</span><span class="kw">:</span><span class="at"> wordpress:cli</span></span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true"></a><span class="at">    </span><span class="fu">user</span><span class="kw">:</span><span class="at"> </span><span class="st">"33:33"</span></span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true"></a><span class="at">    </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> ./wordpress:/var/www/html</span></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true"></a><span class="at">    </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_HOST</span><span class="kw">:</span><span class="at"> db</span></span>
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_USER</span><span class="kw">:</span><span class="at"> wordpress</span></span>
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_PASSWORD</span><span class="kw">:</span><span class="at"> wordpress</span></span>
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_NAME</span><span class="kw">:</span><span class="at"> wordpress</span></span>
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true"></a><span class="at">    </span><span class="fu">depends_on</span><span class="kw">:</span></span>
<span id="cb1-39"><a href="#cb1-39" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> db</span></span>
<span id="cb1-40"><a href="#cb1-40" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> wordpress</span></span>
<span id="cb1-41"><a href="#cb1-41" aria-hidden="true"></a></span>
<span id="cb1-42"><a href="#cb1-42" aria-hidden="true"></a><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb1-43"><a href="#cb1-43" aria-hidden="true"></a><span class="at">  </span><span class="fu">db_data</span><span class="kw">:</span></span></code></pre>
</div>



<h4 class="wp-block-heading" id="launch-environment">Launch Environment</h4>



<div class="sourceCode" id="cb2">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true"></a><span class="co"># Start all services</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="ex">docker-compose</span> up -d</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true"></a></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true"></a><span class="co"># Check status</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a><span class="ex">docker-compose</span> ps</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a><span class="co"># Access WordPress</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a><span class="ex">open</span> http://localhost:8080</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true"></a></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true"></a><span class="co"># View logs</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true"></a><span class="ex">docker-compose</span> logs -f wordpress</span></code></pre>
</div>



<h4 class="wp-block-heading" id="run-wp-cli-in-container">Run WP-CLI in Container</h4>



<div class="sourceCode" id="cb3">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true"></a><span class="co"># Execute WP-CLI commands</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp --info</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true"></a><span class="co"># Install WordPress core</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp core install <span class="kw">\</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a>    <span class="ex">--url</span>=localhost:8080 <span class="kw">\</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a>    <span class="ex">--title</span>=<span class="st">"Docker WordPress"</span> <span class="kw">\</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a>    <span class="ex">--admin_user</span>=admin <span class="kw">\</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a>    <span class="ex">--admin_password</span>=admin <span class="kw">\</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a>    <span class="ex">--admin_email</span>=admin@example.com</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true"></a></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true"></a><span class="co"># Install plugins</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp plugin install contact-form-7 --activate</span></code></pre>
</div>



<p>Learn more about <a href="https://docs.docker.com/compose/">Docker Compose</a> configuration.</p>



<h3 class="wp-block-heading" id="advanced-config">Advanced Docker Configuration</h3>



<p>Enhance your Docker WordPress environment with professional features.</p>



<h4 class="wp-block-heading" id="multiple-php-versions">Multiple PHP Versions</h4>



<div class="sourceCode" id="cb4">
<pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co"># docker-compose.yml with PHP version control</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="fu">version</span><span class="kw">:</span><span class="at"> </span><span class="st">"3.8"</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a><span class="fu">services</span><span class="kw">:</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a><span class="at">  </span><span class="fu">wordpress-php74</span><span class="kw">:</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a><span class="at">    </span><span class="fu">image</span><span class="kw">:</span><span class="at"> wordpress:php7.4-apache</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a><span class="at">    </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="st">"8074:80"</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true"></a><span class="at">    </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_HOST</span><span class="kw">:</span><span class="at"> db</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_NAME</span><span class="kw">:</span><span class="at"> wp_php74</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true"></a><span class="at">    </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> ./wp-php74:/var/www/html</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true"></a></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true"></a><span class="at">  </span><span class="fu">wordpress-php80</span><span class="kw">:</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true"></a><span class="at">    </span><span class="fu">image</span><span class="kw">:</span><span class="at"> wordpress:php8.0-apache</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true"></a><span class="at">    </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="st">"8080:80"</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true"></a><span class="at">    </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_HOST</span><span class="kw">:</span><span class="at"> db</span></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_NAME</span><span class="kw">:</span><span class="at"> wp_php80</span></span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true"></a><span class="at">    </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> ./wp-php80:/var/www/html</span></span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true"></a></span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true"></a><span class="at">  </span><span class="fu">wordpress-php81</span><span class="kw">:</span></span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true"></a><span class="at">    </span><span class="fu">image</span><span class="kw">:</span><span class="at"> wordpress:php8.1-apache</span></span>
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true"></a><span class="at">    </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="st">"8081:80"</span></span>
<span id="cb4-29"><a href="#cb4-29" aria-hidden="true"></a><span class="at">    </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb4-30"><a href="#cb4-30" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_HOST</span><span class="kw">:</span><span class="at"> db</span></span>
<span id="cb4-31"><a href="#cb4-31" aria-hidden="true"></a><span class="at">      </span><span class="fu">WORDPRESS_DB_NAME</span><span class="kw">:</span><span class="at"> wp_php81</span></span>
<span id="cb4-32"><a href="#cb4-32" aria-hidden="true"></a><span class="at">    </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb4-33"><a href="#cb4-33" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> ./wp-php81:/var/www/html</span></span></code></pre>
</div>



<p><strong>Use Case</strong>: Test plugin compatibility across PHP versions simultaneously.</p>



<h4 class="wp-block-heading" id="custom-php-configuration">Custom PHP Configuration</h4>



<div class="sourceCode" id="cb5">
<pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="co"># docker-compose.yml with custom PHP settings</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a><span class="fu">services</span><span class="kw">:</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a><span class="at">  </span><span class="fu">wordpress</span><span class="kw">:</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a><span class="at">    </span><span class="fu">image</span><span class="kw">:</span><span class="at"> wordpress:latest</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a><span class="at">    </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> ./wordpress:/var/www/html</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> ./php.ini:/usr/local/etc/php/conf.d/custom.ini</span></span></code></pre>
</div>



<p><strong>php.ini</strong>:</p>



<div class="sourceCode" id="cb6">
<pre class="sourceCode ini"><code class="sourceCode ini"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="dt">upload_max_filesize </span><span class="ot">=</span><span class="st"> 64M</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="dt">post_max_size </span><span class="ot">=</span><span class="st"> 64M</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a><span class="dt">memory_limit </span><span class="ot">=</span><span class="st"> 256M</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a><span class="dt">max_execution_time </span><span class="ot">=</span><span class="st"> </span><span class="dv">300</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a><span class="dt">display_errors </span><span class="ot">=</span><span class="st"> </span><span class="kw">On</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a><span class="dt">error_reporting </span><span class="ot">=</span><span class="st"> </span><span class="kw">E_ALL</span></span></code></pre>
</div>



<h4 class="wp-block-heading" id="add-phpmyadmin">Add PHPMyAdmin</h4>



<div class="sourceCode" id="cb7">
<pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="fu">services</span><span class="kw">:</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="at">  </span><span class="fu">phpmyadmin</span><span class="kw">:</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a><span class="at">    </span><span class="fu">image</span><span class="kw">:</span><span class="at"> phpmyadmin:latest</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a><span class="at">    </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="st">"8081:80"</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a><span class="at">    </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a><span class="at">      </span><span class="fu">PMA_HOST</span><span class="kw">:</span><span class="at"> db</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a><span class="at">      </span><span class="fu">PMA_USER</span><span class="kw">:</span><span class="at"> wordpress</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a><span class="at">      </span><span class="fu">PMA_PASSWORD</span><span class="kw">:</span><span class="at"> wordpress</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a><span class="at">    </span><span class="fu">depends_on</span><span class="kw">:</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> db</span></span></code></pre>
</div>



<p>Access PHPMyAdmin at <code>http://localhost:8081</code>.</p>



<h3 class="wp-block-heading" id="automated-setup">Automated WordPress Setup</h3>



<p>Create scripts for one-command WordPress environment initialization.</p>



<h4 class="wp-block-heading" id="complete-setup-script">Complete Setup Script</h4>



<div class="sourceCode" id="cb8">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true"></a><span class="co"># wp-docker-init.sh - Initialize WordPress Docker environment</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true"></a></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true"></a><span class="kw">set</span> <span class="ex">-e</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a><span class="va">PROJECT_NAME=</span><span class="st">"</span><span class="va">${1:-</span>wordpress-project<span class="va">}</span><span class="st">"</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a><span class="va">ADMIN_USER=</span><span class="st">"admin"</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a><span class="va">ADMIN_PASS=</span><span class="st">"admin"</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a><span class="va">ADMIN_EMAIL=</span><span class="st">"admin@example.com"</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true"></a></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Initializing WordPress Docker environment: </span><span class="va">$PROJECT_NAME</span><span class="st">"</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true"></a></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true"></a><span class="co"># Create project directory</span></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true"></a><span class="fu">mkdir</span> -p <span class="st">"</span><span class="va">$PROJECT_NAME</span><span class="st">"</span></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true"></a><span class="bu">cd</span> <span class="st">"</span><span class="va">$PROJECT_NAME</span><span class="st">"</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true"></a></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true"></a><span class="co"># Create docker-compose.yml</span></span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true"></a><span class="fu">cat</span> <span class="op">&gt;</span> docker-compose.yml <span class="op">&lt;&lt;'EOF'</span></span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true"></a>version: '3.8'</span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true"></a>services:</span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true"></a>  wordpress:</span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true"></a>    image: wordpress:latest</span>
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true"></a>    ports:</span>
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true"></a>      - "8080:80"</span>
<span id="cb8-25"><a href="#cb8-25" aria-hidden="true"></a>    environment:</span>
<span id="cb8-26"><a href="#cb8-26" aria-hidden="true"></a>      WORDPRESS_DB_HOST: db</span>
<span id="cb8-27"><a href="#cb8-27" aria-hidden="true"></a>      WORDPRESS_DB_USER: wordpress</span>
<span id="cb8-28"><a href="#cb8-28" aria-hidden="true"></a>      WORDPRESS_DB_PASSWORD: wordpress</span>
<span id="cb8-29"><a href="#cb8-29" aria-hidden="true"></a>      WORDPRESS_DB_NAME: wordpress</span>
<span id="cb8-30"><a href="#cb8-30" aria-hidden="true"></a>    volumes:</span>
<span id="cb8-31"><a href="#cb8-31" aria-hidden="true"></a>      - ./wordpress:/var/www/html</span>
<span id="cb8-32"><a href="#cb8-32" aria-hidden="true"></a>    depends_on:</span>
<span id="cb8-33"><a href="#cb8-33" aria-hidden="true"></a>      - db</span>
<span id="cb8-34"><a href="#cb8-34" aria-hidden="true"></a></span>
<span id="cb8-35"><a href="#cb8-35" aria-hidden="true"></a>  db:</span>
<span id="cb8-36"><a href="#cb8-36" aria-hidden="true"></a>    image: mysql:8.0</span>
<span id="cb8-37"><a href="#cb8-37" aria-hidden="true"></a>    environment:</span>
<span id="cb8-38"><a href="#cb8-38" aria-hidden="true"></a>      MYSQL_DATABASE: wordpress</span>
<span id="cb8-39"><a href="#cb8-39" aria-hidden="true"></a>      MYSQL_USER: wordpress</span>
<span id="cb8-40"><a href="#cb8-40" aria-hidden="true"></a>      MYSQL_PASSWORD: wordpress</span>
<span id="cb8-41"><a href="#cb8-41" aria-hidden="true"></a>      MYSQL_ROOT_PASSWORD: rootpassword</span>
<span id="cb8-42"><a href="#cb8-42" aria-hidden="true"></a>    volumes:</span>
<span id="cb8-43"><a href="#cb8-43" aria-hidden="true"></a>      - db_data:/var/lib/mysql</span>
<span id="cb8-44"><a href="#cb8-44" aria-hidden="true"></a></span>
<span id="cb8-45"><a href="#cb8-45" aria-hidden="true"></a>  wpcli:</span>
<span id="cb8-46"><a href="#cb8-46" aria-hidden="true"></a>    image: wordpress:cli</span>
<span id="cb8-47"><a href="#cb8-47" aria-hidden="true"></a>    user: "33:33"</span>
<span id="cb8-48"><a href="#cb8-48" aria-hidden="true"></a>    volumes:</span>
<span id="cb8-49"><a href="#cb8-49" aria-hidden="true"></a>      - ./wordpress:/var/www/html</span>
<span id="cb8-50"><a href="#cb8-50" aria-hidden="true"></a>    environment:</span>
<span id="cb8-51"><a href="#cb8-51" aria-hidden="true"></a>      WORDPRESS_DB_HOST: db</span>
<span id="cb8-52"><a href="#cb8-52" aria-hidden="true"></a>      WORDPRESS_DB_USER: wordpress</span>
<span id="cb8-53"><a href="#cb8-53" aria-hidden="true"></a>      WORDPRESS_DB_PASSWORD: wordpress</span>
<span id="cb8-54"><a href="#cb8-54" aria-hidden="true"></a>      WORDPRESS_DB_NAME: wordpress</span>
<span id="cb8-55"><a href="#cb8-55" aria-hidden="true"></a>    depends_on:</span>
<span id="cb8-56"><a href="#cb8-56" aria-hidden="true"></a>      - db</span>
<span id="cb8-57"><a href="#cb8-57" aria-hidden="true"></a>      - wordpress</span>
<span id="cb8-58"><a href="#cb8-58" aria-hidden="true"></a></span>
<span id="cb8-59"><a href="#cb8-59" aria-hidden="true"></a>volumes:</span>
<span id="cb8-60"><a href="#cb8-60" aria-hidden="true"></a>  db_data:</span>
<span id="cb8-61"><a href="#cb8-61" aria-hidden="true"></a><span class="op">EOF</span></span>
<span id="cb8-62"><a href="#cb8-62" aria-hidden="true"></a></span>
<span id="cb8-63"><a href="#cb8-63" aria-hidden="true"></a><span class="co"># Start containers</span></span>
<span id="cb8-64"><a href="#cb8-64" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Starting Docker containers..."</span></span>
<span id="cb8-65"><a href="#cb8-65" aria-hidden="true"></a><span class="ex">docker-compose</span> up -d</span>
<span id="cb8-66"><a href="#cb8-66" aria-hidden="true"></a></span>
<span id="cb8-67"><a href="#cb8-67" aria-hidden="true"></a><span class="co"># Wait for WordPress to be ready</span></span>
<span id="cb8-68"><a href="#cb8-68" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Waiting for WordPress..."</span></span>
<span id="cb8-69"><a href="#cb8-69" aria-hidden="true"></a><span class="fu">sleep</span> 10</span>
<span id="cb8-70"><a href="#cb8-70" aria-hidden="true"></a></span>
<span id="cb8-71"><a href="#cb8-71" aria-hidden="true"></a><span class="co"># Install WordPress</span></span>
<span id="cb8-72"><a href="#cb8-72" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Installing WordPress..."</span></span>
<span id="cb8-73"><a href="#cb8-73" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp core install <span class="kw">\</span></span>
<span id="cb8-74"><a href="#cb8-74" aria-hidden="true"></a>    <span class="ex">--url</span>=localhost:8080 <span class="kw">\</span></span>
<span id="cb8-75"><a href="#cb8-75" aria-hidden="true"></a>    <span class="ex">--title</span>=<span class="st">"</span><span class="va">$PROJECT_NAME</span><span class="st">"</span> <span class="kw">\</span></span>
<span id="cb8-76"><a href="#cb8-76" aria-hidden="true"></a>    <span class="ex">--admin_user</span>=<span class="st">"</span><span class="va">$ADMIN_USER</span><span class="st">"</span> <span class="kw">\</span></span>
<span id="cb8-77"><a href="#cb8-77" aria-hidden="true"></a>    <span class="ex">--admin_password</span>=<span class="st">"</span><span class="va">$ADMIN_PASS</span><span class="st">"</span> <span class="kw">\</span></span>
<span id="cb8-78"><a href="#cb8-78" aria-hidden="true"></a>    <span class="ex">--admin_email</span>=<span class="st">"</span><span class="va">$ADMIN_EMAIL</span><span class="st">"</span></span>
<span id="cb8-79"><a href="#cb8-79" aria-hidden="true"></a></span>
<span id="cb8-80"><a href="#cb8-80" aria-hidden="true"></a><span class="co"># Install essential plugins</span></span>
<span id="cb8-81"><a href="#cb8-81" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Installing plugins..."</span></span>
<span id="cb8-82"><a href="#cb8-82" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp plugin install <span class="kw">\</span></span>
<span id="cb8-83"><a href="#cb8-83" aria-hidden="true"></a>    <span class="ex">contact-form-7</span> <span class="kw">\</span></span>
<span id="cb8-84"><a href="#cb8-84" aria-hidden="true"></a>    <span class="ex">yoast-seo</span> <span class="kw">\</span></span>
<span id="cb8-85"><a href="#cb8-85" aria-hidden="true"></a>    <span class="ex">wordfence</span> <span class="kw">\</span></span>
<span id="cb8-86"><a href="#cb8-86" aria-hidden="true"></a>    <span class="ex">--activate</span></span>
<span id="cb8-87"><a href="#cb8-87" aria-hidden="true"></a></span>
<span id="cb8-88"><a href="#cb8-88" aria-hidden="true"></a><span class="co"># Install theme</span></span>
<span id="cb8-89"><a href="#cb8-89" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Installing theme..."</span></span>
<span id="cb8-90"><a href="#cb8-90" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp theme install astra --activate</span>
<span id="cb8-91"><a href="#cb8-91" aria-hidden="true"></a></span>
<span id="cb8-92"><a href="#cb8-92" aria-hidden="true"></a><span class="co"># Set permalinks</span></span>
<span id="cb8-93"><a href="#cb8-93" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp rewrite structure <span class="st">'/%postname%/'</span></span>
<span id="cb8-94"><a href="#cb8-94" aria-hidden="true"></a></span>
<span id="cb8-95"><a href="#cb8-95" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ WordPress environment ready!"</span></span>
<span id="cb8-96"><a href="#cb8-96" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"URL: http://localhost:8080"</span></span>
<span id="cb8-97"><a href="#cb8-97" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Admin: </span><span class="va">$ADMIN_USER</span><span class="st"> / </span><span class="va">$ADMIN_PASS</span><span class="st">"</span></span></code></pre>
</div>



<p>Run it:</p>



<div class="sourceCode" id="cb9">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true"></a><span class="fu">chmod</span> +x wp-docker-init.sh</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="ex">./wp-docker-init.sh</span> my-project</span></code></pre>
</div>



<h4 class="wp-block-heading" id="development-helper-scripts">Development Helper Scripts</h4>



<div class="sourceCode" id="cb10">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true"></a><span class="co"># wp.sh - WP-CLI wrapper for Docker</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp <span class="st">"</span><span class="va">$@</span><span class="st">"</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a><span class="co"># Usage:</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a><span class="fu">chmod</span> +x wp.sh</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a><span class="ex">./wp.sh</span> plugin list</span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a><span class="ex">./wp.sh</span> post create --post_title=<span class="st">"Test"</span> --post_status=publish</span></code></pre>
</div>



<h3 class="wp-block-heading" id="workflows">Docker WordPress Workflows</h3>



<p>Professional development patterns with Docker and WP-CLI.</p>



<h4 class="wp-block-heading" id="import-existing-site">Import Existing Site</h4>



<div class="sourceCode" id="cb11">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true"></a><span class="co"># import-to-docker.sh - Import existing WordPress site</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true"></a></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true"></a><span class="va">BACKUP_SQL=</span><span class="st">"</span><span class="va">$1</span><span class="st">"</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a><span class="va">BACKUP_FILES=</span><span class="st">"</span><span class="va">$2</span><span class="st">"</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a><span class="kw">if</span><span class="bu"> [</span> <span class="ot">-z</span> <span class="st">"</span><span class="va">$BACKUP_SQL</span><span class="st">"</span><span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">"Usage: </span><span class="va">$0</span><span class="st"> &lt;backup.sql&gt; &lt;files.tar.gz&gt;"</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true"></a></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true"></a><span class="co"># Start containers</span></span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true"></a><span class="ex">docker-compose</span> up -d</span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true"></a><span class="fu">sleep</span> 10</span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true"></a></span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true"></a><span class="co"># Import database</span></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Importing database..."</span></span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true"></a><span class="ex">docker-compose</span> exec -T db mysql -uwordpress -pwordpress wordpress <span class="op">&lt;</span> <span class="st">"</span><span class="va">$BACKUP_SQL</span><span class="st">"</span></span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true"></a></span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true"></a><span class="co"># Extract files</span></span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Extracting files..."</span></span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true"></a><span class="fu">tar</span> -xzf <span class="st">"</span><span class="va">$BACKUP_FILES</span><span class="st">"</span> -C ./wordpress/</span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true"></a></span>
<span id="cb11-24"><a href="#cb11-24" aria-hidden="true"></a><span class="co"># Update URLs</span></span>
<span id="cb11-25"><a href="#cb11-25" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Updating URLs..."</span></span>
<span id="cb11-26"><a href="#cb11-26" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp search-replace <span class="kw">\</span></span>
<span id="cb11-27"><a href="#cb11-27" aria-hidden="true"></a>    <span class="st">'https://oldsite.com'</span> <span class="st">'http://localhost:8080'</span> <span class="kw">\</span></span>
<span id="cb11-28"><a href="#cb11-28" aria-hidden="true"></a>    <span class="ex">--skip-columns</span>=guid</span>
<span id="cb11-29"><a href="#cb11-29" aria-hidden="true"></a></span>
<span id="cb11-30"><a href="#cb11-30" aria-hidden="true"></a><span class="co"># Flush cache</span></span>
<span id="cb11-31"><a href="#cb11-31" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp cache flush</span>
<span id="cb11-32"><a href="#cb11-32" aria-hidden="true"></a></span>
<span id="cb11-33"><a href="#cb11-33" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Site imported successfully"</span></span></code></pre>
</div>



<h4 class="wp-block-heading" id="export-for-production">Export for Production</h4>



<div class="sourceCode" id="cb12">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a><span class="co"># export-from-docker.sh</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true"></a></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true"></a><span class="va">DATE=$(</span><span class="fu">date</span> +%Y%m%d<span class="va">)</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true"></a></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Exporting WordPress from Docker..."</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true"></a><span class="co"># Export database</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp db export - <span class="op">&gt;</span> <span class="st">"backup-</span><span class="va">$DATE</span><span class="st">.sql"</span></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true"></a></span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true"></a><span class="co"># Compress database</span></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true"></a><span class="fu">gzip</span> <span class="st">"backup-</span><span class="va">$DATE</span><span class="st">.sql"</span></span>
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true"></a></span>
<span id="cb12-14"><a href="#cb12-14" aria-hidden="true"></a><span class="co"># Archive files</span></span>
<span id="cb12-15"><a href="#cb12-15" aria-hidden="true"></a><span class="fu">tar</span> -czf <span class="st">"files-</span><span class="va">$DATE</span><span class="st">.tar.gz"</span> -C ./wordpress .</span>
<span id="cb12-16"><a href="#cb12-16" aria-hidden="true"></a></span>
<span id="cb12-17"><a href="#cb12-17" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Export complete:"</span></span>
<span id="cb12-18"><a href="#cb12-18" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"  Database: backup-</span><span class="va">$DATE</span><span class="st">.sql.gz"</span></span>
<span id="cb12-19"><a href="#cb12-19" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"  Files: files-</span><span class="va">$DATE</span><span class="st">.tar.gz"</span></span></code></pre>
</div>



<h4 class="wp-block-heading" id="reset-development-environment">Reset Development Environment</h4>



<div class="sourceCode" id="cb13">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true"></a><span class="co"># reset-docker-wordpress.sh</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true"></a></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true"></a><span class="bu">read</span> -p <span class="st">"This will destroy all data. Continue? (y/n) "</span> -n 1 -r</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true"></a><span class="bu">echo</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true"></a><span class="kw">if [[</span> <span class="ot">!</span> <span class="va">$REPLY</span> =~ ^[Yy]$<span class="kw"> ]]</span>; <span class="kw">then</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true"></a>    <span class="bu">exit</span> 0</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true"></a></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true"></a><span class="co"># Stop and remove containers</span></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true"></a><span class="ex">docker-compose</span> down -v</span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true"></a></span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true"></a><span class="co"># Remove WordPress files</span></span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true"></a><span class="fu">rm</span> -rf ./wordpress/*</span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true"></a></span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true"></a><span class="co"># Start fresh</span></span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true"></a><span class="ex">docker-compose</span> up -d</span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true"></a><span class="fu">sleep</span> 10</span>
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true"></a></span>
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true"></a><span class="co"># Reinstall</span></span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp core install <span class="kw">\</span></span>
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true"></a>    <span class="ex">--url</span>=localhost:8080 <span class="kw">\</span></span>
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true"></a>    <span class="ex">--title</span>=<span class="st">"Fresh WordPress"</span> <span class="kw">\</span></span>
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true"></a>    <span class="ex">--admin_user</span>=admin <span class="kw">\</span></span>
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true"></a>    <span class="ex">--admin_password</span>=admin <span class="kw">\</span></span>
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true"></a>    <span class="ex">--admin_email</span>=admin@example.com</span>
<span id="cb13-27"><a href="#cb13-27" aria-hidden="true"></a></span>
<span id="cb13-28"><a href="#cb13-28" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Environment reset complete"</span></span></code></pre>
</div>



<h3 class="wp-block-heading" id="testing-debugging">Testing and Debugging</h3>



<p>Debug WordPress issues in Docker containers.</p>



<h4 class="wp-block-heading" id="view-container-logs">View Container Logs</h4>



<div class="sourceCode" id="cb14">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true"></a><span class="co"># WordPress logs</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a><span class="ex">docker-compose</span> logs -f wordpress</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true"></a></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true"></a><span class="co"># Database logs</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a><span class="ex">docker-compose</span> logs -f db</span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true"></a></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true"></a><span class="co"># All logs</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true"></a><span class="ex">docker-compose</span> logs -f</span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true"></a></span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true"></a><span class="co"># Last 50 lines</span></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true"></a><span class="ex">docker-compose</span> logs --tail=50 wordpress</span></code></pre>
</div>



<h4 class="wp-block-heading" id="access-container-shell">Access Container Shell</h4>



<div class="sourceCode" id="cb15">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true"></a><span class="co"># WordPress container bash</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a><span class="ex">docker-compose</span> exec wordpress bash</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true"></a></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true"></a><span class="co"># Once inside, check PHP info</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a><span class="ex">php</span> -i</span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a><span class="co"># Check Apache config</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a><span class="fu">cat</span> /etc/apache2/sites-available/000-default.conf</span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true"></a><span class="co"># View error logs</span></span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true"></a><span class="fu">tail</span> -f /var/log/apache2/error.log</span></code></pre>
</div>



<h4 class="wp-block-heading" id="debug-wp-cli-issues">Debug WP-CLI Issues</h4>



<div class="sourceCode" id="cb16">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true"></a><span class="co"># Check WP-CLI version</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp cli version</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true"></a></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true"></a><span class="co"># Verify WordPress installation</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp core is-installed</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true"></a></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true"></a><span class="co"># Check database connection</span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp db check</span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true"></a></span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true"></a><span class="co"># List environment variables</span></span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli env</span></code></pre>
</div>



<h3 class="wp-block-heading" id="optimization">Performance Optimization</h3>



<p>Improve Docker WordPress performance for development.</p>



<h4 class="wp-block-heading" id="enable-object-caching">Enable Object Caching</h4>



<div class="sourceCode" id="cb17">
<pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true"></a><span class="fu">services</span><span class="kw">:</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true"></a><span class="at">  </span><span class="fu">redis</span><span class="kw">:</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true"></a><span class="at">    </span><span class="fu">image</span><span class="kw">:</span><span class="at"> redis:alpine</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true"></a><span class="at">    </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> </span><span class="st">"6379:6379"</span></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true"></a></span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true"></a><span class="at">  </span><span class="fu">wordpress</span><span class="kw">:</span></span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true"></a><span class="co">    # ... existing config</span></span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true"></a><span class="at">    </span><span class="fu">depends_on</span><span class="kw">:</span></span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> db</span></span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true"></a><span class="at">      </span><span class="kw">-</span><span class="at"> redis</span></span></code></pre>
</div>



<p>Install Redis plugin:</p>



<div class="sourceCode" id="cb18">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp plugin install redis-cache --activate</span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true"></a><span class="ex">docker-compose</span> run --rm wpcli wp redis enable</span></code></pre>
</div>



<h4 class="wp-block-heading" id="optimize-mysql">Optimize MySQL</h4>



<div class="sourceCode" id="cb19">
<pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true"></a><span class="fu">services</span><span class="kw">:</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true"></a><span class="at">  </span><span class="fu">db</span><span class="kw">:</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true"></a><span class="at">    </span><span class="fu">image</span><span class="kw">:</span><span class="at"> mysql:8.0</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true"></a><span class="at">    </span><span class="fu">command</span><span class="kw">:</span><span class="at"> --default-authentication-plugin=mysql_native_password --innodb-buffer-pool-size=256M</span></span></code></pre>
</div>



<h4 class="wp-block-heading" id="volume-performance">Volume Performance</h4>



<div class="sourceCode" id="cb20">
<pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true"></a><span class="co"># Use cached mode for better performance on macOS</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true"></a><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true"></a><span class="at">  </span><span class="kw">-</span><span class="at"> ./wordpress:/var/www/html:cached</span></span></code></pre>
</div>



<p>Learn about <a href="https://docs.docker.com/storage/volumes/">Docker volume performance</a> optimization.</p>



<h3 class="wp-block-heading" id="next-steps">Next Steps</h3>



<p>You now have professional Docker-based WordPress development skills with WP-CLI integration.</p>



<h4 class="wp-block-heading" id="recommended-learning-path">Recommended Learning Path</h4>



<p><strong>Week 1</strong>: Basic Docker setup</p>



<ul class="wp-block-list">
<li>Launch WordPress with Docker Compose</li>



<li>Run WP-CLI in containers</li>



<li>Import/export sites</li>
</ul>



<p><strong>Week 2</strong>: Advanced configuration</p>



<ul class="wp-block-list">
<li>Test multiple PHP versions</li>



<li>Add development tools</li>



<li>Optimize performance</li>
</ul>



<p><strong>Week 3</strong>: Automation</p>



<ul class="wp-block-list">
<li>Build setup scripts</li>



<li>Create helper commands</li>



<li>Implement CI/CD</li>
</ul>



<p><strong>Week 4</strong>: Team workflows</p>



<ul class="wp-block-list">
<li>Share configurations</li>



<li>Document processes</li>



<li>Standardize environments</li>
</ul>



<h4 class="wp-block-heading" id="advanced-topics">Advanced Topics</h4>



<ol class="wp-block-list">
<li><strong><a href="#">Docker Multi-Stage Builds</a></strong> &#8211; Optimized production images</li>



<li><strong><a href="#">Kubernetes WordPress</a></strong> &#8211; Container orchestration</li>



<li><strong><a href="#">CI/CD Pipelines</a></strong> &#8211; Automated deployment</li>
</ol>



<h4 class="wp-block-heading" id="get-more-resources">Get More Resources</h4>



<p><strong><a href="#">Download Docker WordPress configs</a></strong> including:</p>



<ul class="wp-block-list">
<li>Complete docker-compose files</li>



<li>Setup automation scripts</li>



<li>Development tools</li>
</ul>



<p><strong><a href="/#get-started">Join our email course</a></strong> for:</p>



<ul class="wp-block-list">
<li>Weekly WP-CLI tutorials</li>



<li>Docker best practices</li>



<li>Modern WordPress DevOps</li>
</ul>



<h3 class="wp-block-heading" id="conclusion">Conclusion</h3>



<p>Docker and WP-CLI together create the ultimate WordPress development environment—fast, consistent, isolated, and completely reproducible across your entire team.</p>



<p>What we covered:</p>



<p>✅ Docker Compose WordPress configuration <br>✅ WP-CLI integration in containers <br>✅ Advanced Docker features (multiple PHP versions, tools) <br>✅ Automated setup and teardown scripts <br>✅ Import/export workflows <br>✅ Testing, debugging, and optimization</p>



<p>Master these techniques, and you’ll never waste time on local WordPress setup again—spin up fresh environments in seconds, test across PHP versions, and share identical setups with your team.</p>



<p><strong>Ready for more?</strong> Learn <a href="#">WordPress CI/CD pipelines</a> or <a href="#">Kubernetes deployment</a>.</p>



<p><strong>Questions about Docker WordPress with WP-CLI?</strong> Drop a comment below!</p>



<p><strong>Found this helpful?</strong> Share with other WordPress developers.</p>
<p>The post <a href="https://wpclimastery.com/blog/wordpress-development-with-docker-and-wp-cli-complete-setup-guide/">WordPress Development with Docker and WP-CLI: Complete Setup Guide</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Multi-Environment WordPress Management with WP-CLI (Dev/Staging/Prod)</title>
		<link>https://wpclimastery.com/blog/multi-environment-wordpress-management-with-wp-cli-dev-staging-prod/</link>
					<comments>https://wpclimastery.com/blog/multi-environment-wordpress-management-with-wp-cli-dev-staging-prod/#respond</comments>
		
		<dc:creator><![CDATA[Krasen]]></dc:creator>
		<pubDate>Mon, 24 Nov 2025 11:16:24 +0000</pubDate>
				<category><![CDATA[WordPress DevOps]]></category>
		<category><![CDATA[dev staging production]]></category>
		<category><![CDATA[environment synchronization]]></category>
		<category><![CDATA[wordpress environments]]></category>
		<category><![CDATA[wordpress workflow]]></category>
		<category><![CDATA[wp-cli environment management]]></category>
		<guid isPermaLink="false">https://wpclimastery.com/?p=222</guid>

					<description><![CDATA[<p>Professional WordPress development requires managing multiple environments to test changes safely before deploying to production. WP-CLI streamlines environment management, enabling efficient synchronization, configuration control, and deployment workflows that minimize downtime...</p>
<p>The post <a href="https://wpclimastery.com/blog/multi-environment-wordpress-management-with-wp-cli-dev-staging-prod/">Multi-Environment WordPress Management with WP-CLI (Dev/Staging/Prod)</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Professional WordPress development requires managing multiple environments to test changes safely before deploying to production. WP-CLI streamlines environment management, enabling efficient synchronization, configuration control, and deployment workflows that minimize downtime and reduce errors.</p>



<h2 class="wp-block-heading" id="understanding-wordpress-environment-architecture">Understanding WordPress Environment Architecture</h2>



<p>A robust WordPress workflow typically includes three distinct environments: development for building new features, staging for testing changes in production-like conditions, and production where live users interact with your site.</p>



<p>Each environment serves a specific purpose. Development allows rapid iteration without affecting users, staging validates changes before release, and production serves actual traffic. Proper environment management ensures code flows smoothly through this pipeline while maintaining data integrity and security.</p>



<h2 class="wp-block-heading" id="setting-up-environment-specific-configurations">Setting Up Environment-Specific Configurations</h2>



<p>WordPress 5.5 introduced native environment type support through the&nbsp;<code>WP_ENVIRONMENT_TYPE</code>&nbsp;constant. WP-CLI leverages this for environment-aware operations.</p>



<pre class="wp-block-code"><code><em># Set environment type in wp-config.php</em>
wp config set WP_ENVIRONMENT_TYPE 'development' --raw

<em># Verify current environment</em>
wp config get WP_ENVIRONMENT_TYPE

<em># Different values: local, development, staging, production</em>
</code></pre>



<p>Create environment-specific configuration files:</p>



<pre class="wp-block-code"><code><em># wp-config-local.php for development</em>
cat &gt; wp-config-local.php &lt;&lt; 'EOF'
&lt;?php
// Development settings
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', true);
define('SCRIPT_DEBUG', true);
define('SAVEQUERIES', true);

// Disable caching in development
define('WP_CACHE', false);

// Development database
define('DB_NAME', 'wp_dev');
define('DB_USER', 'dev_user');
define('DB_PASSWORD', 'dev_password');
define('DB_HOST', 'localhost');
EOF

<em># wp-config-staging.php</em>
cat &gt; wp-config-staging.php &lt;&lt; 'EOF'
&lt;?php
// Staging settings
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);

// Prevent search engine indexing
define('DISCOURAGE_SEARCH', true);

// Staging database
define('DB_NAME', 'wp_staging');
define('DB_USER', 'staging_user');
define('DB_PASSWORD', 'staging_password');
define('DB_HOST', 'localhost');
EOF

<em># wp-config-production.php</em>
cat &gt; wp-config-production.php &lt;&lt; 'EOF'
&lt;?php
// Production settings
define('WP_DEBUG', false);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', false);

// Security enhancements
define('DISALLOW_FILE_EDIT', true);
define('DISALLOW_FILE_MODS', false);
define('FORCE_SSL_ADMIN', true);

// Production database
define('DB_NAME', 'wp_production');
define('DB_USER', 'prod_user');
define('DB_PASSWORD', 'secure_production_password');
define('DB_HOST', 'production-db.example.com');
EOF
</code></pre>



<h2 class="wp-block-heading" id="database-synchronization-strategies">Database Synchronization Strategies</h2>



<p>Syncing databases between environments requires careful handling of environment-specific data like URLs, file paths, and credentials.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># sync-db-staging-to-dev.sh - Sync staging database to development</em>

set -euo pipefail

STAGING_PATH="/var/www/staging"
DEV_PATH="/var/www/development"
BACKUP_DIR="/backups"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)

echo "Starting database sync from staging to development"

<em># Backup development database before sync</em>
echo "Backing up development database..."
wp --path="$DEV_PATH" db export "$BACKUP_DIR/dev-pre-sync-$TIMESTAMP.sql"

<em># Export staging database</em>
echo "Exporting staging database..."
wp --path="$STAGING_PATH" db export "$BACKUP_DIR/staging-export-$TIMESTAMP.sql"

<em># Get staging URL</em>
STAGING_URL=$(wp --path="$STAGING_PATH" option get siteurl)

<em># Get development URL</em>
DEV_URL=$(wp --path="$DEV_PATH" option get siteurl)

<em># Import to development</em>
echo "Importing to development..."
wp --path="$DEV_PATH" db import "$BACKUP_DIR/staging-export-$TIMESTAMP.sql"

<em># Update URLs</em>
echo "Updating URLs from $STAGING_URL to $DEV_URL"
wp --path="$DEV_PATH" search-replace "$STAGING_URL" "$DEV_URL" --all-tables

<em># Update file paths if different</em>
STAGING_PATH_ABS=$(wp --path="$STAGING_PATH" eval 'echo ABSPATH;')
DEV_PATH_ABS=$(wp --path="$DEV_PATH" eval 'echo ABSPATH;')

if &#91; "$STAGING_PATH_ABS" != "$DEV_PATH_ABS" ]; then
    echo "Updating file paths..."
    wp --path="$DEV_PATH" search-replace "$STAGING_PATH_ABS" "$DEV_PATH_ABS" --all-tables
fi

<em># Flush cache</em>
wp --path="$DEV_PATH" cache flush

<em># Update permalinks</em>
wp --path="$DEV_PATH" rewrite flush

echo "Database sync completed successfully"
</code></pre>



<h2 class="wp-block-heading" id="selective-data-synchronization">Selective Data Synchronization</h2>



<p>Production databases often contain sensitive user data that shouldn&#8217;t be copied to development environments. Implement selective synchronization:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># sync-selective.sh - Sync database with data sanitization</em>

WP_PATH="/var/www/development"
TEMP_SQL="/tmp/staging-sanitized.sql"

<em># Export from staging (on staging server)</em>
ssh staging@staging.example.com "wp --path=/var/www/html db export - " &gt; "$TEMP_SQL"

<em># Import to development</em>
wp --path="$WP_PATH" db import "$TEMP_SQL"

<em># Sanitize sensitive data in development</em>
echo "Sanitizing user data..."

<em># Anonymize user emails (except admin)</em>
wp --path="$WP_PATH" db query "
UPDATE wp_users
SET user_email = CONCAT('user', ID, '@example.local')
WHERE ID != 1
"

<em># Clear user passwords</em>
wp --path="$WP_PATH" db query "
UPDATE wp_users
SET user_pass = MD5(CONCAT('dev_password_', ID))
"

<em># Remove personal data from comments</em>
wp --path="$WP_PATH" db query "
UPDATE wp_comments
SET comment_author_email = CONCAT('commenter', comment_ID, '@example.local'),
    comment_author_IP = '127.0.0.1'
"

<em># Clear transients and cached data</em>
wp --path="$WP_PATH" transient delete --all

<em># Update site URLs</em>
wp --path="$WP_PATH" search-replace 'https://staging.example.com' 'http://dev.local' --all-tables

echo "Selective sync completed with data sanitization"
rm "$TEMP_SQL"
</code></pre>



<h2 class="wp-block-heading" id="file-synchronization">File Synchronization</h2>



<p>Sync uploads and plugin/theme files between environments while excluding unnecessary files.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># sync-files.sh - Sync WordPress files between environments</em>

SOURCE_PATH="/var/www/staging"
DEST_PATH="/var/www/development"

<em># Sync uploads directory</em>
echo "Syncing uploads..."
rsync -avz --delete \
    --exclude='cache/' \
    --exclude='backup/' \
    "$SOURCE_PATH/wp-content/uploads/" \
    "$DEST_PATH/wp-content/uploads/"

<em># Sync plugins (optional - usually managed by version control)</em>
echo "Syncing plugins..."
rsync -avz --delete \
    --exclude='*/cache' \
    --exclude='*.log' \
    "$SOURCE_PATH/wp-content/plugins/" \
    "$DEST_PATH/wp-content/plugins/"

<em># Sync themes</em>
echo "Syncing themes..."
rsync -avz --delete \
    "$SOURCE_PATH/wp-content/themes/" \
    "$DEST_PATH/wp-content/themes/"

<em># Set correct permissions</em>
chown -R www-data:www-data "$DEST_PATH/wp-content"
find "$DEST_PATH/wp-content" -type d -exec chmod 755 {} \;
find "$DEST_PATH/wp-content" -type f -exec chmod 644 {} \;

echo "File sync completed"
</code></pre>



<h2 class="wp-block-heading" id="environment-aware-deployment-scripts">Environment-Aware Deployment Scripts</h2>



<p>Create intelligent deployment scripts that adapt based on target environment.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># deploy.sh - Environment-aware deployment script</em>

set -euo pipefail

<em># Configuration</em>
ENVIRONMENTS=("development" "staging" "production")
TARGET_ENV="$1"

if &#91;&#91; ! " ${ENVIRONMENTS&#91;@]} " =~ " ${TARGET_ENV} " ]]; then
    echo "Error: Invalid environment. Use: development, staging, or production"
    exit 1
fi

<em># Load environment-specific configuration</em>
case $TARGET_ENV in
    development)
        WP_PATH="/var/www/development"
        BACKUP_RETENTION=7
        UPDATE_CORE=true
        UPDATE_PLUGINS=true
        ;;
    staging)
        WP_PATH="/var/www/staging"
        BACKUP_RETENTION=14
        UPDATE_CORE=true
        UPDATE_PLUGINS=true
        ;;
    production)
        WP_PATH="/var/www/html"
        BACKUP_RETENTION=30
        UPDATE_CORE=false  <em># Manual approval required</em>
        UPDATE_PLUGINS=false
        ;;
esac

echo "Deploying to: $TARGET_ENV"

<em># Pre-deployment checks</em>
echo "Running pre-deployment checks..."

<em># Verify WordPress is installed</em>
if ! wp --path="$WP_PATH" core is-installed; then
    echo "Error: WordPress not properly installed at $WP_PATH"
    exit 1
fi

<em># Check database connection</em>
if ! wp --path="$WP_PATH" db check; then
    echo "Error: Database connection failed"
    exit 1
fi

<em># Create backup</em>
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="/backups/$TARGET_ENV"
mkdir -p "$BACKUP_DIR"

echo "Creating backup..."
wp --path="$WP_PATH" db export "$BACKUP_DIR/pre-deploy-$TIMESTAMP.sql"

<em># Clean old backups</em>
find "$BACKUP_DIR" -name "pre-deploy-*.sql" -mtime +$BACKUP_RETENTION -delete

<em># Maintenance mode</em>
if &#91;&#91; "$TARGET_ENV" == "production" ]]; then
    echo "Enabling maintenance mode..."
    wp --path="$WP_PATH" maintenance-mode activate
fi

<em># Deploy code (assuming Git deployment)</em>
echo "Pulling latest code..."
cd "$WP_PATH"
git pull origin "$(git rev-parse --abbrev-ref HEAD)"

<em># Install/update dependencies</em>
if &#91; -f "composer.json" ]; then
    composer install --no-dev --optimize-autoloader
fi

<em># Run database migrations if needed</em>
if wp --path="$WP_PATH" plugin is-installed wp-migrate-db; then
    wp --path="$WP_PATH" migratedb migrate
fi

<em># Update plugins and core if allowed</em>
if &#91;&#91; "$UPDATE_PLUGINS" == true ]]; then
    echo "Updating plugins..."
    wp --path="$WP_PATH" plugin update --all --exclude=custom-plugin
fi

if &#91;&#91; "$UPDATE_CORE" == true ]]; then
    echo "Updating WordPress core..."
    wp --path="$WP_PATH" core update --minor
fi

<em># Clear caches</em>
echo "Clearing caches..."
wp --path="$WP_PATH" cache flush
wp --path="$WP_PATH" transient delete --expired

if wp --path="$WP_PATH" plugin is-installed wp-super-cache --status=active; then
    wp --path="$WP_PATH" cache flush
fi

<em># Regenerate assets if needed</em>
if &#91; -f "package.json" ]; then
    npm run build
fi

<em># Update permalinks</em>
wp --path="$WP_PATH" rewrite flush

<em># Deactivate maintenance mode</em>
if &#91;&#91; "$TARGET_ENV" == "production" ]]; then
    echo "Disabling maintenance mode..."
    wp --path="$WP_PATH" maintenance-mode deactivate
fi

<em># Post-deployment verification</em>
echo "Running post-deployment verification..."

<em># Check site is accessible</em>
SITE_URL=$(wp --path="$WP_PATH" option get siteurl)
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$SITE_URL")

if &#91; "$HTTP_CODE" != "200" ]; then
    echo "Warning: Site returned HTTP $HTTP_CODE"

    if &#91;&#91; "$TARGET_ENV" == "production" ]]; then
        echo "Rolling back..."
        wp --path="$WP_PATH" db import "$BACKUP_DIR/pre-deploy-$TIMESTAMP.sql"
        exit 1
    fi
fi

<em># Verify core integrity</em>
if ! wp --path="$WP_PATH" core verify-checksums; then
    echo "Warning: Core checksum verification failed"
fi

echo "Deployment to $TARGET_ENV completed successfully"

<em># Send notification (production only)</em>
if &#91;&#91; "$TARGET_ENV" == "production" ]]; then
    echo "Deployment completed at $(date)" | mail -s "Production Deployment Success" admin@example.com
fi
</code></pre>



<h2 class="wp-block-heading" id="configuration-management">Configuration Management</h2>



<p>Manage environment-specific settings without hardcoding sensitive values.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># config-manager.sh - Environment configuration management</em>

WP_PATH="/var/www/html"
ENV_FILE=".env"

<em># Load environment variables from .env file</em>
if &#91; -f "$ENV_FILE" ]; then
    export $(cat "$ENV_FILE" | grep -v '^#' | xargs)
fi

<em># Set configuration based on environment</em>
set_config() {
    local key="$1"
    local value="$2"
    local type="${3:-constant}"

    if &#91;&#91; "$type" == "constant" ]]; then
        wp --path="$WP_PATH" config set "$key" "$value" --raw
    else
        wp --path="$WP_PATH" option update "$key" "$value"
    fi
}

<em># Get current environment</em>
CURRENT_ENV=$(wp --path="$WP_PATH" config get WP_ENVIRONMENT_TYPE 2&gt;/dev/null || echo "production")

echo "Configuring for environment: $CURRENT_ENV"

<em># Apply environment-specific settings</em>
case $CURRENT_ENV in
    development|local)
        set_config WP_DEBUG true constant
        set_config WP_DEBUG_LOG true constant
        set_config WP_DEBUG_DISPLAY true constant
        set_config SCRIPT_DEBUG true constant
        set_config SAVEQUERIES true constant

        <em># Development plugins</em>
        wp --path="$WP_PATH" plugin activate query-monitor
        wp --path="$WP_PATH" plugin activate debug-bar
        ;;

    staging)
        set_config WP_DEBUG true constant
        set_config WP_DEBUG_LOG true constant
        set_config WP_DEBUG_DISPLAY false constant
        set_config SCRIPT_DEBUG false constant

        <em># Disable search indexing</em>
        set_config blog_public 0 option

        <em># Staging-specific plugins</em>
        wp --path="$WP_PATH" plugin deactivate google-analytics
        ;;

    production)
        set_config WP_DEBUG false constant
        set_config WP_DEBUG_LOG false constant
        set_config WP_DEBUG_DISPLAY false constant
        set_config SCRIPT_DEBUG false constant

        <em># Security settings</em>
        set_config DISALLOW_FILE_EDIT true constant
        set_config FORCE_SSL_ADMIN true constant

        <em># Enable search indexing</em>
        set_config blog_public 1 option

        <em># Deactivate development plugins</em>
        wp --path="$WP_PATH" plugin deactivate query-monitor --quiet 2&gt;/dev/null || true
        wp --path="$WP_PATH" plugin deactivate debug-bar --quiet 2&gt;/dev/null || true
        ;;
esac

echo "Configuration applied for $CURRENT_ENV environment"
</code></pre>



<h2 class="wp-block-heading" id="automated-environment-testing">Automated Environment Testing</h2>



<p>Verify environment configuration and functionality before accepting deployments.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># test-environment.sh - Automated environment testing</em>

WP_PATH="/var/www/staging"

echo "=== Environment Testing ==="

<em># Test WordPress installation</em>
echo "Testing WordPress installation..."
if wp --path="$WP_PATH" core is-installed; then
    echo "✓ WordPress is installed"
else
    echo "✗ WordPress installation check failed"
    exit 1
fi

<em># Test database connection</em>
echo "Testing database connection..."
if wp --path="$WP_PATH" db check; then
    echo "✓ Database connection successful"
else
    echo "✗ Database connection failed"
    exit 1
fi

<em># Verify core files</em>
echo "Verifying core files..."
if wp --path="$WP_PATH" core verify-checksums; then
    echo "✓ Core files verified"
else
    echo "⚠ Core checksum verification failed"
fi

<em># Check for required plugins</em>
REQUIRED_PLUGINS=("wordpress-seo" "wordfence")
echo "Checking required plugins..."
for plugin in "${REQUIRED_PLUGINS&#91;@]}"; do
    if wp --path="$WP_PATH" plugin is-installed "$plugin"; then
        STATUS=$(wp --path="$WP_PATH" plugin status "$plugin" 2&gt;&amp;1 | grep -o "Active" || echo "Inactive")
        echo "✓ $plugin: $STATUS"
    else
        echo "✗ $plugin: Not installed"
    fi
done

<em># Test URL accessibility</em>
echo "Testing site accessibility..."
SITE_URL=$(wp --path="$WP_PATH" option get siteurl)
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$SITE_URL")

if &#91; "$HTTP_CODE" == "200" ]; then
    echo "✓ Site accessible (HTTP $HTTP_CODE)"
else
    echo "✗ Site returned HTTP $HTTP_CODE"
    exit 1
fi

<em># Check permalink structure</em>
echo "Testing permalinks..."
wp --path="$WP_PATH" rewrite flush
if wp --path="$WP_PATH" rewrite list &gt; /dev/null 2&gt;&amp;1; then
    echo "✓ Permalinks configured"
else
    echo "✗ Permalink configuration issue"
fi

<em># Performance checks</em>
echo "Running performance checks..."
DB_SIZE=$(wp --path="$WP_PATH" db size --human-readable --format=json | jq -r '.Size')
echo "Database size: $DB_SIZE"

AUTOLOAD=$(wp --path="$WP_PATH" db query "SELECT CONCAT(ROUND(SUM(LENGTH(option_value))/1024/1024,2),'MB') as size FROM wp_options WHERE autoload='yes'" --skip-column-names)
echo "Autoload size: $AUTOLOAD"

echo "=== Environment Testing Complete ==="
</code></pre>



<h2 class="wp-block-heading" id="related-links">Related Links</h2>



<ul class="wp-block-list">
<li><a href="https://developer.wordpress.org/cli/commands/">WP-CLI Environment Commands</a></li>



<li><a href="https://developer.wordpress.org/themes/advanced-topics/wordpress-development-best-practices/">WordPress Development Best Practices</a></li>



<li><a href="https://developer.wordpress.org/advanced-administration/">Version Control for WordPress</a></li>



<li><a href="https://developer.wordpress.org/cli/commands/db/">Database Management with WP-CLI</a></li>



<li><a href="https://www.deployhq.com/wordpress">Continuous Deployment Strategies</a></li>
</ul>
<p>The post <a href="https://wpclimastery.com/blog/multi-environment-wordpress-management-with-wp-cli-dev-staging-prod/">Multi-Environment WordPress Management with WP-CLI (Dev/Staging/Prod)</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wpclimastery.com/blog/multi-environment-wordpress-management-with-wp-cli-dev-staging-prod/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
