<?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 Automation Archives - WP-CLI Mastery</title>
	<atom:link href="https://wpclimastery.com/blog/category/wordpress-automation/feed/" rel="self" type="application/rss+xml" />
	<link>https://wpclimastery.com/blog/category/wordpress-automation/</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 Automation Archives - WP-CLI Mastery</title>
	<link>https://wpclimastery.com/blog/category/wordpress-automation/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Build a WordPress Deployment Pipeline with WP-CLI and Bash</title>
		<link>https://wpclimastery.com/blog/build-a-wordpress-deployment-pipeline-with-wp-cli-and-bash/</link>
		
		<dc:creator><![CDATA[Krasen]]></dc:creator>
		<pubDate>Tue, 10 Feb 2026 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Automation]]></category>
		<category><![CDATA[automated wordpress deployment]]></category>
		<category><![CDATA[deployment pipeline]]></category>
		<category><![CDATA[wordpress deployment]]></category>
		<category><![CDATA[wordpress devops]]></category>
		<category><![CDATA[wp-cli deployment]]></category>
		<guid isPermaLink="false">https://wpclimastery.com/?p=164</guid>

					<description><![CDATA[<p>Deploying WordPress changes manually means FTPing files, manually importing databases, running SQL updates, and crossing your fingers that nothing breaks. One mistake during deployment takes your production site offline during...</p>
<p>The post <a href="https://wpclimastery.com/blog/build-a-wordpress-deployment-pipeline-with-wp-cli-and-bash/">Build a WordPress Deployment Pipeline with WP-CLI and Bash</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Deploying WordPress changes manually means FTPing files, manually importing databases, running SQL updates, and crossing your fingers that nothing breaks. One mistake during deployment takes your production site offline during peak traffic hours.</p>



<p>A WordPress deployment pipeline automates the entire process—code changes flow from development through testing to production automatically, with safety checks, automated testing, rollback capability, and zero downtime. Deploy confidently dozens of times per day.</p>



<p>In this guide, you’ll build a complete WordPress deployment pipeline using WP-CLI and Bash, from basic scripts to production-ready systems used by professional WordPress development teams.</p>



<h3 class="wp-block-heading" id="why-pipeline">Why Build a Deployment Pipeline?</h3>



<p><a href="https://wordpress.org/support/article/updating-wordpress/">Manual WordPress deployments</a> don’t scale and introduce human error at every step.</p>



<h4 class="wp-block-heading" id="problems-with-manual-deployment">Problems with Manual Deployment</h4>



<p><strong>Error-prone</strong>: Manual file transfers and database updates cause mistakes.</p>



<p><strong>Downtime</strong>: Sites go offline during deployments affecting users and revenue.</p>



<p><strong>No testing</strong>: Changes deploy directly to production without validation.</p>



<p><strong>Slow</strong>: Manual deployments take 30-60 minutes of focused work.</p>



<p><strong>No rollback</strong>: Fixing failed deployments requires manual restoration from backups.</p>



<h4 class="wp-block-heading" id="deployment-pipeline-benefits">Deployment Pipeline Benefits</h4>



<p><strong>Automated</strong>: One command deploys entire release from development to production.</p>



<p><strong>Zero downtime</strong>: Users never see maintenance pages or errors.</p>



<p><strong>Tested</strong>: Automated tests catch issues before they reach production.</p>



<p><strong>Fast</strong>: Complete deployments in under 5 minutes instead of hours.</p>



<p><strong>Reversible</strong>: Instant rollback to previous version if issues occur.</p>



<p>According to <a href="https://cloud.google.com/devops/state-of-devops">DevOps Research</a>, teams with deployment pipelines deploy 200x more frequently with 24x faster recovery times.</p>



<h3 class="wp-block-heading" id="pipeline-components">Deployment Pipeline Components</h3>



<p>Understand the stages of a WordPress deployment pipeline.</p>



<h4 class="wp-block-heading" id="basic-pipeline-stages">Basic Pipeline Stages</h4>



<div class="sourceCode" id="cb1">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="ex">1.</span> Development  → Code changes made locally</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="ex">2.</span> Testing      → Automated tests verify changes</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a><span class="ex">3.</span> Staging      → Deploy to staging environment</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="ex">4.</span> Validation   → Manual/automated verification</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a><span class="ex">5.</span> Production   → Deploy to live site</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a><span class="ex">6.</span> Monitoring   → Track deployment health</span></code></pre>
</div>



<h4 class="wp-block-heading" id="essential-pipeline-elements">Essential Pipeline Elements</h4>



<p><strong>Version Control</strong>: Git tracks all changes and enables rollback.</p>



<p><strong>Database Migrations</strong>: Automated schema and data updates.</p>



<p><strong>Asset Building</strong>: Compile CSS, minify JavaScript, optimize images.</p>



<p><strong>Environment Config</strong>: Different settings for dev/staging/production.</p>



<p><strong>Health Checks</strong>: Verify deployment succeeded before completing.</p>



<p><strong>Rollback Strategy</strong>: Quick recovery from failed deployments.</p>



<p>Learn about <a href="https://www.atlassian.com/continuous-delivery/continuous-deployment">continuous deployment</a> principles.</p>



<h3 class="wp-block-heading" id="basic-deployment">Basic Deployment Script</h3>



<p>Start with a simple automated deployment workflow.</p>



<h4 class="wp-block-heading" id="manual-deployment-process">Manual Deployment Process</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">#!/bin/bash</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="co"># deploy-wordpress.sh - Basic deployment script</span></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="kw">set</span> <span class="ex">-euo</span> pipefail</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true"></a><span class="va">REPO_URL=</span><span class="st">"git@github.com:yourcompany/wordpress-site.git"</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true"></a><span class="va">DEPLOY_PATH=</span><span class="st">"/var/www/production"</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a><span class="va">BRANCH=</span><span class="st">"main"</span></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="bu">cd</span> <span class="st">"</span><span class="va">$DEPLOY_PATH</span><span class="st">"</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true"></a></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Starting deployment..."</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true"></a></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true"></a><span class="co"># Pull latest code</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true"></a><span class="fu">git</span> fetch origin</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true"></a><span class="fu">git</span> reset --hard <span class="st">"origin/</span><span class="va">$BRANCH</span><span class="st">"</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true"></a></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true"></a><span class="co"># Install/update dependencies</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true"></a><span class="ex">composer</span> install --no-dev --optimize-autoloader</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true"></a></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true"></a><span class="co"># Run database migrations</span></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true"></a><span class="ex">wp</span> db migrate</span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true"></a></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true"></a><span class="co"># Clear caches</span></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true"></a><span class="ex">wp</span> cache flush</span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true"></a><span class="ex">wp</span> rewrite flush</span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true"></a></span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true"></a><span class="co"># Build assets (if needed)</span></span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true"></a><span class="ex">npm</span> run build</span>
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true"></a></span>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Deployment complete"</span></span></code></pre>
</div>



<h4 class="wp-block-heading" id="deployment-with-backup">Deployment with Backup</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">#!/bin/bash</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="co"># deploy-with-backup.sh</span></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="kw">set</span> <span class="ex">-euo</span> pipefail</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a><span class="va">DEPLOY_PATH=</span><span class="st">"/var/www/production"</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a><span class="va">BACKUP_DIR=</span><span class="st">"/backups/deployments"</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a><span class="va">DATE=$(</span><span class="fu">date</span> +%Y%m%d_%H%M%S<span class="va">)</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true"></a></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true"></a><span class="bu">cd</span> <span class="st">"</span><span class="va">$DEPLOY_PATH</span><span class="st">"</span></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"># Create pre-deployment backup</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Creating backup..."</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true"></a><span class="fu">mkdir</span> -p <span class="st">"</span><span class="va">$BACKUP_DIR</span><span class="st">"</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true"></a></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true"></a><span class="ex">wp</span> db export <span class="st">"</span><span class="va">$BACKUP_DIR</span><span class="st">/db-</span><span class="va">$DATE</span><span class="st">.sql.gz"</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true"></a><span class="fu">git</span> rev-parse HEAD <span class="op">&gt;</span> <span class="st">"</span><span class="va">$BACKUP_DIR</span><span class="st">/git-commit-</span><span class="va">$DATE</span><span class="st">.txt"</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true"></a></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true"></a><span class="co"># Deploy</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Deploying..."</span></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true"></a><span class="fu">git</span> pull origin main</span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true"></a></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true"></a><span class="ex">composer</span> install --no-dev</span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true"></a></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true"></a><span class="co"># Database updates</span></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true"></a><span class="ex">wp</span> db migrate <span class="kw">||</span> <span class="kw">{</span></span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">"Migration failed, rolling back..."</span></span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true"></a>    <span class="ex">wp</span> db import <span class="st">"</span><span class="va">$BACKUP_DIR</span><span class="st">/db-</span><span class="va">$DATE</span><span class="st">.sql.gz"</span></span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true"></a>    <span class="fu">git</span> reset --hard <span class="va">$(</span><span class="fu">cat</span> <span class="st">"</span><span class="va">$BACKUP_DIR</span><span class="st">/git-commit-</span><span class="va">$DATE</span><span class="st">.txt"</span><span class="va">)</span></span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true"></a><span class="kw">}</span></span>
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true"></a></span>
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true"></a><span class="co"># Clear caches</span></span>
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true"></a><span class="ex">wp</span> cache flush</span>
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true"></a></span>
<span id="cb3-36"><a href="#cb3-36" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Deployment complete"</span></span>
<span id="cb3-37"><a href="#cb3-37" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Backup: </span><span class="va">$BACKUP_DIR</span><span class="st">/db-</span><span class="va">$DATE</span><span class="st">.sql.gz"</span></span></code></pre>
</div>



<h3 class="wp-block-heading" id="staging-to-production">Staging to Production Pipeline</h3>



<p>Deploy changes from staging environment to production safely.</p>



<h4 class="wp-block-heading" id="complete-staging-deploy">Complete Staging Deploy</h4>



<div class="sourceCode" id="cb4">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="co"># deploy-staging-to-production.sh</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="kw">set</span> <span class="ex">-euo</span> pipefail</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a><span class="va">STAGING_PATH=</span><span class="st">"/var/www/staging"</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a><span class="va">PROD_PATH=</span><span class="st">"/var/www/production"</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true"></a><span class="va">BACKUP_DIR=</span><span class="st">"/backups/production"</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true"></a><span class="va">DATE=$(</span><span class="fu">date</span> +%Y%m%d_%H%M%S<span class="va">)</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true"></a></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"=== Staging to Production Deployment ==="</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true"></a></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true"></a><span class="co"># Step 1: Backup production</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Step 1/6: Backing up production..."</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true"></a><span class="bu">cd</span> <span class="st">"</span><span class="va">$PROD_PATH</span><span class="st">"</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true"></a></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true"></a><span class="ex">wp</span> db export <span class="st">"</span><span class="va">$BACKUP_DIR</span><span class="st">/db-</span><span class="va">$DATE</span><span class="st">.sql.gz"</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true"></a><span class="fu">tar</span> -czf <span class="st">"</span><span class="va">$BACKUP_DIR</span><span class="st">/files-</span><span class="va">$DATE</span><span class="st">.tar.gz"</span> <span class="kw">\</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true"></a>    <span class="ex">wp-content/plugins</span> <span class="kw">\</span></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true"></a>    <span class="ex">wp-content/themes</span> <span class="kw">\</span></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true"></a>    <span class="ex">wp-content/uploads</span></span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true"></a></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Backup complete"</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="co"># Step 2: Get staging code version</span></span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Step 2/6: Getting staging version..."</span></span>
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true"></a><span class="bu">cd</span> <span class="st">"</span><span class="va">$STAGING_PATH</span><span class="st">"</span></span>
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true"></a><span class="va">STAGING_COMMIT=$(</span><span class="fu">git</span> rev-parse HEAD<span class="va">)</span></span>
<span id="cb4-29"><a href="#cb4-29" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Staging commit: </span><span class="va">$STAGING_COMMIT</span><span class="st">"</span></span>
<span id="cb4-30"><a href="#cb4-30" aria-hidden="true"></a></span>
<span id="cb4-31"><a href="#cb4-31" aria-hidden="true"></a><span class="co"># Step 3: Deploy code to production</span></span>
<span id="cb4-32"><a href="#cb4-32" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Step 3/6: Deploying code..."</span></span>
<span id="cb4-33"><a href="#cb4-33" aria-hidden="true"></a><span class="bu">cd</span> <span class="st">"</span><span class="va">$PROD_PATH</span><span class="st">"</span></span>
<span id="cb4-34"><a href="#cb4-34" aria-hidden="true"></a></span>
<span id="cb4-35"><a href="#cb4-35" aria-hidden="true"></a><span class="co"># Enable maintenance mode</span></span>
<span id="cb4-36"><a href="#cb4-36" aria-hidden="true"></a><span class="ex">wp</span> maintenance-mode activate</span>
<span id="cb4-37"><a href="#cb4-37" aria-hidden="true"></a></span>
<span id="cb4-38"><a href="#cb4-38" aria-hidden="true"></a><span class="fu">git</span> fetch origin</span>
<span id="cb4-39"><a href="#cb4-39" aria-hidden="true"></a><span class="fu">git</span> reset --hard <span class="st">"</span><span class="va">$STAGING_COMMIT</span><span class="st">"</span></span>
<span id="cb4-40"><a href="#cb4-40" aria-hidden="true"></a></span>
<span id="cb4-41"><a href="#cb4-41" aria-hidden="true"></a><span class="co"># Step 4: Install dependencies</span></span>
<span id="cb4-42"><a href="#cb4-42" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Step 4/6: Installing dependencies..."</span></span>
<span id="cb4-43"><a href="#cb4-43" aria-hidden="true"></a><span class="ex">composer</span> install --no-dev --no-interaction</span>
<span id="cb4-44"><a href="#cb4-44" aria-hidden="true"></a></span>
<span id="cb4-45"><a href="#cb4-45" aria-hidden="true"></a><span class="co"># Build assets</span></span>
<span id="cb4-46"><a href="#cb4-46" aria-hidden="true"></a><span class="ex">npm</span> run build</span>
<span id="cb4-47"><a href="#cb4-47" aria-hidden="true"></a></span>
<span id="cb4-48"><a href="#cb4-48" aria-hidden="true"></a><span class="co"># Step 5: Run database migrations</span></span>
<span id="cb4-49"><a href="#cb4-49" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Step 5/6: Running database migrations..."</span></span>
<span id="cb4-50"><a href="#cb4-50" aria-hidden="true"></a><span class="kw">if</span> ! <span class="ex">wp</span> db migrate<span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb4-51"><a href="#cb4-51" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">"✗ Migration failed, rolling back..."</span></span>
<span id="cb4-52"><a href="#cb4-52" aria-hidden="true"></a>    <span class="ex">wp</span> db import <span class="st">"</span><span class="va">$BACKUP_DIR</span><span class="st">/db-</span><span class="va">$DATE</span><span class="st">.sql.gz"</span></span>
<span id="cb4-53"><a href="#cb4-53" aria-hidden="true"></a>    <span class="ex">wp</span> maintenance-mode deactivate</span>
<span id="cb4-54"><a href="#cb4-54" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb4-55"><a href="#cb4-55" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb4-56"><a href="#cb4-56" aria-hidden="true"></a></span>
<span id="cb4-57"><a href="#cb4-57" aria-hidden="true"></a><span class="co"># Step 6: Final checks and cleanup</span></span>
<span id="cb4-58"><a href="#cb4-58" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Step 6/6: Final checks..."</span></span>
<span id="cb4-59"><a href="#cb4-59" aria-hidden="true"></a></span>
<span id="cb4-60"><a href="#cb4-60" aria-hidden="true"></a><span class="co"># Verify WordPress works</span></span>
<span id="cb4-61"><a href="#cb4-61" aria-hidden="true"></a><span class="kw">if</span> ! <span class="ex">wp</span> core is-installed<span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb4-62"><a href="#cb4-62" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">"✗ WordPress check failed, rolling back..."</span></span>
<span id="cb4-63"><a href="#cb4-63" aria-hidden="true"></a>    <span class="ex">wp</span> db import <span class="st">"</span><span class="va">$BACKUP_DIR</span><span class="st">/db-</span><span class="va">$DATE</span><span class="st">.sql.gz"</span></span>
<span id="cb4-64"><a href="#cb4-64" aria-hidden="true"></a>    <span class="ex">wp</span> maintenance-mode deactivate</span>
<span id="cb4-65"><a href="#cb4-65" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb4-66"><a href="#cb4-66" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb4-67"><a href="#cb4-67" aria-hidden="true"></a></span>
<span id="cb4-68"><a href="#cb4-68" aria-hidden="true"></a><span class="co"># Clear caches</span></span>
<span id="cb4-69"><a href="#cb4-69" aria-hidden="true"></a><span class="ex">wp</span> cache flush</span>
<span id="cb4-70"><a href="#cb4-70" aria-hidden="true"></a><span class="ex">wp</span> rewrite flush</span>
<span id="cb4-71"><a href="#cb4-71" aria-hidden="true"></a></span>
<span id="cb4-72"><a href="#cb4-72" aria-hidden="true"></a><span class="co"># Disable maintenance mode</span></span>
<span id="cb4-73"><a href="#cb4-73" aria-hidden="true"></a><span class="ex">wp</span> maintenance-mode deactivate</span>
<span id="cb4-74"><a href="#cb4-74" aria-hidden="true"></a></span>
<span id="cb4-75"><a href="#cb4-75" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"=== Deployment Complete ==="</span></span>
<span id="cb4-76"><a href="#cb4-76" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Deployed commit: </span><span class="va">$STAGING_COMMIT</span><span class="st">"</span></span>
<span id="cb4-77"><a href="#cb4-77" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Backup: </span><span class="va">$BACKUP_DIR</span><span class="st">/db-</span><span class="va">$DATE</span><span class="st">.sql.gz"</span></span></code></pre>
</div>



<h4 class="wp-block-heading" id="automated-validation">Automated Validation</h4>



<div class="sourceCode" id="cb5">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a><span class="co"># validate-deployment.sh</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a><span class="va">SITE_URL=</span><span class="st">"</span><span class="va">$1</span><span class="st">"</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Validating deployment..."</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true"></a><span class="co"># Test homepage</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true"></a><span class="va">HTTP_CODE=$(</span><span class="ex">curl</span> -s -o /dev/null -w <span class="st">"%{http_code}"</span> <span class="st">"</span><span class="va">$SITE_URL</span><span class="st">"</span><span class="va">)</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true"></a><span class="kw">if</span><span class="bu"> [</span> <span class="st">"</span><span class="va">$HTTP_CODE</span><span class="st">"</span> <span class="ot">!=</span> <span class="st">"200"</span><span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">"✗ Homepage failed: HTTP </span><span class="va">$HTTP_CODE</span><span class="st">"</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Homepage accessible"</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true"></a></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true"></a><span class="co"># Test admin</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true"></a><span class="va">ADMIN_CODE=$(</span><span class="ex">curl</span> -s -o /dev/null -w <span class="st">"%{http_code}"</span> <span class="st">"</span><span class="va">$SITE_URL</span><span class="st">/wp-admin/"</span><span class="va">)</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true"></a><span class="kw">if</span><span class="bu"> [</span> <span class="st">"</span><span class="va">$ADMIN_CODE</span><span class="st">"</span> <span class="ot">!=</span> <span class="st">"200"</span><span class="bu"> ]</span> <span class="kw">&amp;&amp;</span><span class="bu"> [</span> <span class="st">"</span><span class="va">$ADMIN_CODE</span><span class="st">"</span> <span class="ot">!=</span> <span class="st">"302"</span><span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">"✗ Admin failed: HTTP </span><span class="va">$ADMIN_CODE</span><span class="st">"</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Admin accessible"</span></span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true"></a></span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true"></a><span class="co"># Test critical pages</span></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true"></a><span class="kw">for</span> <span class="ex">PAGE</span> in <span class="st">"/about"</span> <span class="st">"/contact"</span> <span class="st">"/shop"</span><span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true"></a>    <span class="va">CODE=$(</span><span class="ex">curl</span> -s -o /dev/null -w <span class="st">"%{http_code}"</span> <span class="st">"</span><span class="va">$SITE_URL$PAGE</span><span class="st">"</span><span class="va">)</span></span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true"></a>    <span class="kw">if</span><span class="bu"> [</span> <span class="st">"</span><span class="va">$CODE</span><span class="st">"</span> <span class="ot">=</span> <span class="st">"200"</span><span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">"✓ </span><span class="va">$PAGE</span><span class="st"> accessible"</span></span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true"></a>    <span class="kw">else</span></span>
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">"⚠ </span><span class="va">$PAGE</span><span class="st"> returned HTTP </span><span class="va">$CODE</span><span class="st">"</span></span>
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true"></a><span class="kw">done</span></span>
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true"></a></span>
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Validation complete"</span></span></code></pre>
</div>



<h3 class="wp-block-heading" id="zero-downtime">Zero-Downtime Deployment</h3>



<p>Deploy without taking the site offline.</p>



<h4 class="wp-block-heading" id="symlink-strategy">Symlink Strategy</h4>



<div class="sourceCode" id="cb6">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="co"># zero-downtime-deploy.sh</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a><span class="kw">set</span> <span class="ex">-euo</span> pipefail</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a><span class="va">DEPLOY_BASE=</span><span class="st">"/var/www/releases"</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a><span class="va">CURRENT_LINK=</span><span class="st">"/var/www/current"</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></a><span class="va">SHARED_DIR=</span><span class="st">"/var/www/shared"</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true"></a><span class="va">RELEASE_ID=$(</span><span class="fu">date</span> +%Y%m%d_%H%M%S<span class="va">)</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true"></a><span class="va">RELEASE_PATH=</span><span class="st">"</span><span class="va">$DEPLOY_BASE</span><span class="st">/</span><span class="va">$RELEASE_ID</span><span class="st">"</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true"></a></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"=== Zero-Downtime Deployment ==="</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Release ID: </span><span class="va">$RELEASE_ID</span><span class="st">"</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true"></a></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true"></a><span class="co"># Create new release directory</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true"></a><span class="fu">mkdir</span> -p <span class="st">"</span><span class="va">$RELEASE_PATH</span><span class="st">"</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true"></a><span class="bu">cd</span> <span class="st">"</span><span class="va">$RELEASE_PATH</span><span class="st">"</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true"></a></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true"></a><span class="co"># Clone code</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true"></a><span class="fu">git</span> clone --depth 1 git@github.com:yourcompany/site.git .</span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true"></a></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true"></a><span class="co"># Link shared directories</span></span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true"></a><span class="fu">ln</span> -s <span class="st">"</span><span class="va">$SHARED_DIR</span><span class="st">/wp-content/uploads"</span> wp-content/uploads</span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true"></a><span class="fu">ln</span> -s <span class="st">"</span><span class="va">$SHARED_DIR</span><span class="st">/wp-config.php"</span> wp-config.php</span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true"></a></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true"></a><span class="co"># Install dependencies</span></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true"></a><span class="ex">composer</span> install --no-dev</span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true"></a></span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true"></a><span class="co"># Build assets</span></span>
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true"></a><span class="ex">npm</span> run build</span>
<span id="cb6-31"><a href="#cb6-31" aria-hidden="true"></a></span>
<span id="cb6-32"><a href="#cb6-32" aria-hidden="true"></a><span class="co"># Database migrations (on shared database)</span></span>
<span id="cb6-33"><a href="#cb6-33" aria-hidden="true"></a><span class="bu">cd</span> <span class="st">"</span><span class="va">$SHARED_DIR</span><span class="st">"</span></span>
<span id="cb6-34"><a href="#cb6-34" aria-hidden="true"></a><span class="ex">wp</span> db migrate --path=<span class="st">"</span><span class="va">$RELEASE_PATH</span><span class="st">"</span></span>
<span id="cb6-35"><a href="#cb6-35" aria-hidden="true"></a></span>
<span id="cb6-36"><a href="#cb6-36" aria-hidden="true"></a><span class="co"># Atomic switch to new release</span></span>
<span id="cb6-37"><a href="#cb6-37" aria-hidden="true"></a><span class="fu">ln</span> -sfn <span class="st">"</span><span class="va">$RELEASE_PATH</span><span class="st">"</span> <span class="st">"</span><span class="va">$CURRENT_LINK</span><span class="st">"</span></span>
<span id="cb6-38"><a href="#cb6-38" aria-hidden="true"></a></span>
<span id="cb6-39"><a href="#cb6-39" aria-hidden="true"></a><span class="co"># Webserver points to /var/www/current</span></span>
<span id="cb6-40"><a href="#cb6-40" aria-hidden="true"></a></span>
<span id="cb6-41"><a href="#cb6-41" aria-hidden="true"></a><span class="co"># Cleanup old releases (keep last 5)</span></span>
<span id="cb6-42"><a href="#cb6-42" aria-hidden="true"></a><span class="bu">cd</span> <span class="st">"</span><span class="va">$DEPLOY_BASE</span><span class="st">"</span></span>
<span id="cb6-43"><a href="#cb6-43" aria-hidden="true"></a><span class="fu">ls</span> -t <span class="kw">|</span> <span class="fu">tail</span> -n +6 <span class="kw">|</span> <span class="fu">xargs</span> rm -rf</span>
<span id="cb6-44"><a href="#cb6-44" aria-hidden="true"></a></span>
<span id="cb6-45"><a href="#cb6-45" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Deployment complete"</span></span>
<span id="cb6-46"><a href="#cb6-46" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Current release: </span><span class="va">$RELEASE_ID</span><span class="st">"</span></span></code></pre>
</div>



<h4 class="wp-block-heading" id="blue-green-deployment">Blue-Green Deployment</h4>



<div class="sourceCode" id="cb7">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="co"># blue-green-deploy.sh</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a><span class="va">BLUE_PATH=</span><span class="st">"/var/www/blue"</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a><span class="va">GREEN_PATH=</span><span class="st">"/var/www/green"</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a><span class="va">CURRENT_LINK=</span><span class="st">"/var/www/current"</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a><span class="co"># Determine inactive environment</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a><span class="kw">if</span><span class="bu"> [</span> <span class="st">"</span><span class="va">$(</span><span class="fu">readlink</span> <span class="va">$CURRENT_LINK)</span><span class="st">"</span> <span class="ot">=</span> <span class="st">"</span><span class="va">$BLUE_PATH</span><span class="st">"</span><span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a>    <span class="va">INACTIVE=</span><span class="st">"</span><span class="va">$GREEN_PATH</span><span class="st">"</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a>    <span class="va">INACTIVE_NAME=</span><span class="st">"green"</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a><span class="kw">else</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a>    <span class="va">INACTIVE=</span><span class="st">"</span><span class="va">$BLUE_PATH</span><span class="st">"</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true"></a>    <span class="va">INACTIVE_NAME=</span><span class="st">"blue"</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true"></a></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Deploying to inactive environment: </span><span class="va">$INACTIVE_NAME</span><span class="st">"</span></span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true"></a></span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true"></a><span class="bu">cd</span> <span class="st">"</span><span class="va">$INACTIVE</span><span class="st">"</span></span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true"></a></span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true"></a><span class="co"># Deploy to inactive</span></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true"></a><span class="fu">git</span> pull origin main</span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true"></a><span class="ex">composer</span> install --no-dev</span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true"></a><span class="ex">npm</span> run build</span>
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true"></a></span>
<span id="cb7-26"><a href="#cb7-26" aria-hidden="true"></a><span class="co"># Test inactive environment</span></span>
<span id="cb7-27"><a href="#cb7-27" aria-hidden="true"></a><span class="kw">if</span> <span class="ex">curl</span> -f <span class="st">"http://localhost:8080"</span> <span class="op">&gt;</span> /dev/null <span class="op">2&gt;&amp;1</span><span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb7-28"><a href="#cb7-28" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">"✓ Inactive environment healthy"</span></span>
<span id="cb7-29"><a href="#cb7-29" aria-hidden="true"></a><span class="kw">else</span></span>
<span id="cb7-30"><a href="#cb7-30" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">"✗ Inactive environment failed health check"</span></span>
<span id="cb7-31"><a href="#cb7-31" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb7-32"><a href="#cb7-32" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb7-33"><a href="#cb7-33" aria-hidden="true"></a></span>
<span id="cb7-34"><a href="#cb7-34" aria-hidden="true"></a><span class="co"># Switch traffic to new version</span></span>
<span id="cb7-35"><a href="#cb7-35" aria-hidden="true"></a><span class="fu">ln</span> -sfn <span class="st">"</span><span class="va">$INACTIVE</span><span class="st">"</span> <span class="st">"</span><span class="va">$CURRENT_LINK</span><span class="st">"</span></span>
<span id="cb7-36"><a href="#cb7-36" aria-hidden="true"></a></span>
<span id="cb7-37"><a href="#cb7-37" aria-hidden="true"></a><span class="co"># Reload webserver</span></span>
<span id="cb7-38"><a href="#cb7-38" aria-hidden="true"></a><span class="ex">systemctl</span> reload nginx</span>
<span id="cb7-39"><a href="#cb7-39" aria-hidden="true"></a></span>
<span id="cb7-40"><a href="#cb7-40" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Switched to </span><span class="va">$INACTIVE_NAME</span><span class="st"> environment"</span></span></code></pre>
</div>



<p>Learn about <a href="https://docs.aws.amazon.com/whitepapers/latest/blue-green-deployments/welcome.html">zero-downtime deployment strategies</a>.</p>



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



<p>Safely manage database schema changes during deployments.</p>



<h4 class="wp-block-heading" id="migration-system">Migration System</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"># db-migrate.sh - Run database migrations</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="va">MIGRATIONS_DIR=</span><span class="st">"/var/www/db/migrations"</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a><span class="va">APPLIED_FILE=</span><span class="st">"/var/www/db/applied-migrations.txt"</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a><span class="fu">touch</span> <span class="st">"</span><span class="va">$APPLIED_FILE</span><span class="st">"</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Running database migrations..."</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="kw">for</span> <span class="ex">MIGRATION</span> in <span class="va">$(</span><span class="fu">ls</span> <span class="st">"</span><span class="va">$MIGRATIONS_DIR</span><span class="st">"</span>/*.sql <span class="kw">|</span> <span class="fu">sort</span><span class="va">)</span><span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true"></a>    <span class="va">MIGRATION_NAME=$(</span><span class="fu">basename</span> <span class="st">"</span><span class="va">$MIGRATION</span><span class="st">"</span><span class="va">)</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true"></a></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true"></a>    <span class="co"># Check if already applied</span></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true"></a>    <span class="kw">if</span> <span class="fu">grep</span> -q <span class="st">"</span><span class="va">$MIGRATION_NAME</span><span class="st">"</span> <span class="st">"</span><span class="va">$APPLIED_FILE</span><span class="st">"</span><span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">"⊘ Skipping: </span><span class="va">$MIGRATION_NAME</span><span class="st"> (already applied)"</span></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true"></a>        <span class="bu">continue</span></span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true"></a></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">"Applying: </span><span class="va">$MIGRATION_NAME</span><span class="st">"</span></span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true"></a></span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true"></a>    <span class="co"># Run migration</span></span>
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ex">wp</span> db query <span class="op">&lt;</span> <span class="st">"</span><span class="va">$MIGRATION</span><span class="st">"</span><span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">"</span><span class="va">$MIGRATION_NAME</span><span class="st">"</span> <span class="op">&gt;&gt;</span> <span class="st">"</span><span class="va">$APPLIED_FILE</span><span class="st">"</span></span>
<span id="cb8-25"><a href="#cb8-25" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">"✓ Applied: </span><span class="va">$MIGRATION_NAME</span><span class="st">"</span></span>
<span id="cb8-26"><a href="#cb8-26" aria-hidden="true"></a>    <span class="kw">else</span></span>
<span id="cb8-27"><a href="#cb8-27" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">"✗ Failed: </span><span class="va">$MIGRATION_NAME</span><span class="st">"</span></span>
<span id="cb8-28"><a href="#cb8-28" aria-hidden="true"></a>        <span class="bu">exit</span> 1</span>
<span id="cb8-29"><a href="#cb8-29" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb8-30"><a href="#cb8-30" aria-hidden="true"></a><span class="kw">done</span></span>
<span id="cb8-31"><a href="#cb8-31" aria-hidden="true"></a></span>
<span id="cb8-32"><a href="#cb8-32" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ All migrations applied"</span></span></code></pre>
</div>



<h4 class="wp-block-heading" id="reversible-migrations">Reversible Migrations</h4>



<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="co"># Migration file structure</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="co"># migrations/001-add-custom-table.sql (up)</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a><span class="co"># migrations/001-add-custom-table-rollback.sql (down)</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a><span class="co"># rollback-migration.sh</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a><span class="va">MIGRATION_ID=</span><span class="st">"</span><span class="va">$1</span><span class="st">"</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a><span class="va">MIGRATIONS_DIR=</span><span class="st">"/var/www/db/migrations"</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true"></a></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true"></a><span class="va">ROLLBACK_FILE=</span><span class="st">"</span><span class="va">$MIGRATIONS_DIR</span><span class="st">/</span><span class="va">${MIGRATION_ID}</span><span class="st">-rollback.sql"</span></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true"></a></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true"></a><span class="kw">if</span><span class="bu"> [</span> <span class="ot">!</span> <span class="ot">-f</span> <span class="st">"</span><span class="va">$ROLLBACK_FILE</span><span class="st">"</span><span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">"✗ Rollback file not found: </span><span class="va">$ROLLBACK_FILE</span><span class="st">"</span></span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true"></a></span>
<span id="cb9-18"><a href="#cb9-18" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Rolling back migration: </span><span class="va">$MIGRATION_ID</span><span class="st">"</span></span>
<span id="cb9-19"><a href="#cb9-19" aria-hidden="true"></a><span class="ex">wp</span> db query <span class="op">&lt;</span> <span class="st">"</span><span class="va">$ROLLBACK_FILE</span><span class="st">"</span></span>
<span id="cb9-20"><a href="#cb9-20" aria-hidden="true"></a></span>
<span id="cb9-21"><a href="#cb9-21" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Rollback complete"</span></span></code></pre>
</div>



<h3 class="wp-block-heading" id="notifications">Deployment Notifications</h3>



<p>Alert teams about deployment status.</p>



<h4 class="wp-block-heading" id="slack-notifications">Slack Notifications</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">#!/bin/bash</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a><span class="co"># notify-deployment.sh</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a><span class="va">SLACK_WEBHOOK=</span><span class="st">"https://hooks.slack.com/services/YOUR/WEBHOOK/URL"</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a><span class="va">STATUS=</span><span class="st">"</span><span class="va">$1</span><span class="st">"</span>  # <span class="ex">success</span> or failure</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a><span class="va">COMMIT=</span><span class="st">"</span><span class="va">$2</span><span class="st">"</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a><span class="va">DEPLOYER=</span><span class="st">"</span><span class="va">$3</span><span class="st">"</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a><span class="kw">if</span><span class="bu"> [</span> <span class="st">"</span><span class="va">$STATUS</span><span class="st">"</span> <span class="ot">=</span> <span class="st">"success"</span><span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a>    <span class="va">COLOR=</span><span class="st">"good"</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a>    <span class="va">MESSAGE=</span><span class="st">"Deployment successful"</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a><span class="kw">else</span></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true"></a>    <span class="va">COLOR=</span><span class="st">"danger"</span></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true"></a>    <span class="va">MESSAGE=</span><span class="st">"Deployment failed"</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true"></a></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true"></a><span class="ex">curl</span> -X POST <span class="st">"</span><span class="va">$SLACK_WEBHOOK</span><span class="st">"</span> <span class="kw">\</span></span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true"></a>    <span class="ex">-H</span> <span class="st">'Content-Type: application/json'</span> <span class="kw">\</span></span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true"></a>    <span class="ex">-d</span> <span class="st">"{</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true"></a><span class="st">        </span><span class="dt">\"</span><span class="st">attachments</span><span class="dt">\"</span><span class="st">: [{</span></span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true"></a><span class="st">            </span><span class="dt">\"</span><span class="st">color</span><span class="dt">\"</span><span class="st">: </span><span class="dt">\"</span><span class="va">$COLOR</span><span class="dt">\"</span><span class="st">,</span></span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true"></a><span class="st">            </span><span class="dt">\"</span><span class="st">title</span><span class="dt">\"</span><span class="st">: </span><span class="dt">\"</span><span class="va">$MESSAGE</span><span class="dt">\"</span><span class="st">,</span></span>
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true"></a><span class="st">            </span><span class="dt">\"</span><span class="st">fields</span><span class="dt">\"</span><span class="st">: [</span></span>
<span id="cb10-24"><a href="#cb10-24" aria-hidden="true"></a><span class="st">                {</span><span class="dt">\"</span><span class="st">title</span><span class="dt">\"</span><span class="st">: </span><span class="dt">\"</span><span class="st">Environment</span><span class="dt">\"</span><span class="st">, </span><span class="dt">\"</span><span class="st">value</span><span class="dt">\"</span><span class="st">: </span><span class="dt">\"</span><span class="st">Production</span><span class="dt">\"</span><span class="st">, </span><span class="dt">\"</span><span class="st">short</span><span class="dt">\"</span><span class="st">: true},</span></span>
<span id="cb10-25"><a href="#cb10-25" aria-hidden="true"></a><span class="st">                {</span><span class="dt">\"</span><span class="st">title</span><span class="dt">\"</span><span class="st">: </span><span class="dt">\"</span><span class="st">Commit</span><span class="dt">\"</span><span class="st">, </span><span class="dt">\"</span><span class="st">value</span><span class="dt">\"</span><span class="st">: </span><span class="dt">\"</span><span class="va">$COMMIT</span><span class="dt">\"</span><span class="st">, </span><span class="dt">\"</span><span class="st">short</span><span class="dt">\"</span><span class="st">: true},</span></span>
<span id="cb10-26"><a href="#cb10-26" aria-hidden="true"></a><span class="st">                {</span><span class="dt">\"</span><span class="st">title</span><span class="dt">\"</span><span class="st">: </span><span class="dt">\"</span><span class="st">Deployed by</span><span class="dt">\"</span><span class="st">, </span><span class="dt">\"</span><span class="st">value</span><span class="dt">\"</span><span class="st">: </span><span class="dt">\"</span><span class="va">$DEPLOYER</span><span class="dt">\"</span><span class="st">, </span><span class="dt">\"</span><span class="st">short</span><span class="dt">\"</span><span class="st">: true}</span></span>
<span id="cb10-27"><a href="#cb10-27" aria-hidden="true"></a><span class="st">            ]</span></span>
<span id="cb10-28"><a href="#cb10-28" aria-hidden="true"></a><span class="st">        }]</span></span>
<span id="cb10-29"><a href="#cb10-29" aria-hidden="true"></a><span class="st">    }"</span></span></code></pre>
</div>



<h4 class="wp-block-heading" id="email-notifications">Email Notifications</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"># email-deployment-report.sh</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">EMAIL=</span><span class="st">"team@example.com"</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a><span class="va">STATUS=</span><span class="st">"</span><span class="va">$1</span><span class="st">"</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a><span class="va">DETAILS=</span><span class="st">"</span><span class="va">$2</span><span class="st">"</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a><span class="va">SUBJECT=</span><span class="st">"WordPress Deployment </span><span class="va">$STATUS</span><span class="st">"</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true"></a></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true"></a><span class="fu">cat</span> <span class="op">&lt;&lt;EOF</span> <span class="kw">|</span> <span class="ex">mail</span> -s <span class="st">"</span><span class="va">$SUBJECT</span><span class="st">"</span> <span class="st">"</span><span class="va">$EMAIL</span><span class="st">"</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true"></a>WordPress Deployment Report</span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true"></a>===========================</span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true"></a></span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true"></a>Status: <span class="va">$STATUS</span></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true"></a>Time: <span class="va">$(</span><span class="fu">date</span><span class="va">)</span></span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true"></a>Server: <span class="va">$(</span><span class="fu">hostname</span><span class="va">)</span></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true"></a>Deployed by: <span class="va">$(</span><span class="fu">whoami</span><span class="va">)</span></span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true"></a></span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true"></a>Details:</span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true"></a><span class="va">$DETAILS</span></span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true"></a></span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true"></a>Automated deployment system</span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true"></a>EOF</span></code></pre>
</div>



<h3 class="wp-block-heading" id="complete-pipeline">Complete Production Pipeline</h3>



<p>Integrate all components into a complete system.</p>



<h4 class="wp-block-heading" id="master-deployment-script">Master Deployment Script</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"># deploy.sh - Complete deployment pipeline</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="kw">set</span> <span class="ex">-euo</span> pipefail</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="va">ENVIRONMENT=</span><span class="st">"</span><span class="va">${1:-</span>staging<span class="va">}</span><span class="st">"</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true"></a><span class="va">LOG_FILE=</span><span class="st">"/var/log/deployments/deploy-</span><span class="va">$(</span><span class="fu">date</span> +%Y%m%d-%H%M%S<span class="va">)</span><span class="st">.log"</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true"></a></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true"></a><span class="fu">mkdir</span> -p <span class="st">"</span><span class="va">$(</span><span class="fu">dirname</span> <span class="va">$LOG_FILE)</span><span class="st">"</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="fu">log()</span> <span class="kw">{</span></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">"[</span><span class="va">$(</span><span class="fu">date</span> <span class="st">'+%Y-%m-%d %H:%M:%S'</span><span class="va">)</span><span class="st">] </span><span class="va">$@</span><span class="st">"</span> <span class="kw">|</span> <span class="fu">tee</span> -a <span class="st">"</span><span class="va">$LOG_FILE</span><span class="st">"</span></span>
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true"></a><span class="kw">}</span></span>
<span id="cb12-14"><a href="#cb12-14" aria-hidden="true"></a></span>
<span id="cb12-15"><a href="#cb12-15" aria-hidden="true"></a><span class="fu">notify()</span> <span class="kw">{</span></span>
<span id="cb12-16"><a href="#cb12-16" aria-hidden="true"></a>    <span class="ex">./notify-deployment.sh</span> <span class="st">"</span><span class="va">$1</span><span class="st">"</span> <span class="st">"</span><span class="va">$2</span><span class="st">"</span> <span class="st">"</span><span class="va">$(</span><span class="fu">whoami</span><span class="va">)</span><span class="st">"</span></span>
<span id="cb12-17"><a href="#cb12-17" aria-hidden="true"></a><span class="kw">}</span></span>
<span id="cb12-18"><a href="#cb12-18" aria-hidden="true"></a></span>
<span id="cb12-19"><a href="#cb12-19" aria-hidden="true"></a><span class="ex">log</span> <span class="st">"=== Deployment Started ==="</span></span>
<span id="cb12-20"><a href="#cb12-20" aria-hidden="true"></a><span class="ex">log</span> <span class="st">"Environment: </span><span class="va">$ENVIRONMENT</span><span class="st">"</span></span>
<span id="cb12-21"><a href="#cb12-21" aria-hidden="true"></a></span>
<span id="cb12-22"><a href="#cb12-22" aria-hidden="true"></a><span class="co"># Pre-deployment checks</span></span>
<span id="cb12-23"><a href="#cb12-23" aria-hidden="true"></a><span class="ex">log</span> <span class="st">"Running pre-deployment checks..."</span></span>
<span id="cb12-24"><a href="#cb12-24" aria-hidden="true"></a><span class="ex">./pre-deploy-check.sh</span> <span class="st">"</span><span class="va">$ENVIRONMENT</span><span class="st">"</span> <span class="kw">||</span> <span class="kw">{</span></span>
<span id="cb12-25"><a href="#cb12-25" aria-hidden="true"></a>    <span class="ex">log</span> <span class="st">"✗ Pre-deployment checks failed"</span></span>
<span id="cb12-26"><a href="#cb12-26" aria-hidden="true"></a>    <span class="ex">notify</span> <span class="st">"failure"</span> <span class="st">"Pre-deployment checks failed"</span></span>
<span id="cb12-27"><a href="#cb12-27" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb12-28"><a href="#cb12-28" aria-hidden="true"></a><span class="kw">}</span></span>
<span id="cb12-29"><a href="#cb12-29" aria-hidden="true"></a></span>
<span id="cb12-30"><a href="#cb12-30" aria-hidden="true"></a><span class="co"># Backup</span></span>
<span id="cb12-31"><a href="#cb12-31" aria-hidden="true"></a><span class="ex">log</span> <span class="st">"Creating backup..."</span></span>
<span id="cb12-32"><a href="#cb12-32" aria-hidden="true"></a><span class="ex">./backup.sh</span> <span class="st">"</span><span class="va">$ENVIRONMENT</span><span class="st">"</span></span>
<span id="cb12-33"><a href="#cb12-33" aria-hidden="true"></a></span>
<span id="cb12-34"><a href="#cb12-34" aria-hidden="true"></a><span class="co"># Deploy code</span></span>
<span id="cb12-35"><a href="#cb12-35" aria-hidden="true"></a><span class="ex">log</span> <span class="st">"Deploying code..."</span></span>
<span id="cb12-36"><a href="#cb12-36" aria-hidden="true"></a><span class="ex">./deploy-code.sh</span> <span class="st">"</span><span class="va">$ENVIRONMENT</span><span class="st">"</span> <span class="kw">||</span> <span class="kw">{</span></span>
<span id="cb12-37"><a href="#cb12-37" aria-hidden="true"></a>    <span class="ex">log</span> <span class="st">"✗ Code deployment failed"</span></span>
<span id="cb12-38"><a href="#cb12-38" aria-hidden="true"></a>    <span class="ex">./rollback.sh</span> <span class="st">"</span><span class="va">$ENVIRONMENT</span><span class="st">"</span></span>
<span id="cb12-39"><a href="#cb12-39" aria-hidden="true"></a>    <span class="ex">notify</span> <span class="st">"failure"</span> <span class="st">"Code deployment failed"</span></span>
<span id="cb12-40"><a href="#cb12-40" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb12-41"><a href="#cb12-41" aria-hidden="true"></a><span class="kw">}</span></span>
<span id="cb12-42"><a href="#cb12-42" aria-hidden="true"></a></span>
<span id="cb12-43"><a href="#cb12-43" aria-hidden="true"></a><span class="co"># Database migrations</span></span>
<span id="cb12-44"><a href="#cb12-44" aria-hidden="true"></a><span class="ex">log</span> <span class="st">"Running database migrations..."</span></span>
<span id="cb12-45"><a href="#cb12-45" aria-hidden="true"></a><span class="ex">./db-migrate.sh</span> <span class="kw">||</span> <span class="kw">{</span></span>
<span id="cb12-46"><a href="#cb12-46" aria-hidden="true"></a>    <span class="ex">log</span> <span class="st">"✗ Database migration failed"</span></span>
<span id="cb12-47"><a href="#cb12-47" aria-hidden="true"></a>    <span class="ex">./rollback.sh</span> <span class="st">"</span><span class="va">$ENVIRONMENT</span><span class="st">"</span></span>
<span id="cb12-48"><a href="#cb12-48" aria-hidden="true"></a>    <span class="ex">notify</span> <span class="st">"failure"</span> <span class="st">"Database migration failed"</span></span>
<span id="cb12-49"><a href="#cb12-49" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb12-50"><a href="#cb12-50" aria-hidden="true"></a><span class="kw">}</span></span>
<span id="cb12-51"><a href="#cb12-51" aria-hidden="true"></a></span>
<span id="cb12-52"><a href="#cb12-52" aria-hidden="true"></a><span class="co"># Post-deployment validation</span></span>
<span id="cb12-53"><a href="#cb12-53" aria-hidden="true"></a><span class="ex">log</span> <span class="st">"Validating deployment..."</span></span>
<span id="cb12-54"><a href="#cb12-54" aria-hidden="true"></a><span class="ex">./validate-deployment.sh</span> <span class="st">"</span><span class="va">$ENVIRONMENT</span><span class="st">"</span> <span class="kw">||</span> <span class="kw">{</span></span>
<span id="cb12-55"><a href="#cb12-55" aria-hidden="true"></a>    <span class="ex">log</span> <span class="st">"✗ Validation failed"</span></span>
<span id="cb12-56"><a href="#cb12-56" aria-hidden="true"></a>    <span class="ex">./rollback.sh</span> <span class="st">"</span><span class="va">$ENVIRONMENT</span><span class="st">"</span></span>
<span id="cb12-57"><a href="#cb12-57" aria-hidden="true"></a>    <span class="ex">notify</span> <span class="st">"failure"</span> <span class="st">"Deployment validation failed"</span></span>
<span id="cb12-58"><a href="#cb12-58" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb12-59"><a href="#cb12-59" aria-hidden="true"></a><span class="kw">}</span></span>
<span id="cb12-60"><a href="#cb12-60" aria-hidden="true"></a></span>
<span id="cb12-61"><a href="#cb12-61" aria-hidden="true"></a><span class="co"># Success</span></span>
<span id="cb12-62"><a href="#cb12-62" aria-hidden="true"></a><span class="ex">log</span> <span class="st">"=== Deployment Complete ==="</span></span>
<span id="cb12-63"><a href="#cb12-63" aria-hidden="true"></a><span class="ex">notify</span> <span class="st">"success"</span> <span class="st">"Deployment to </span><span class="va">$ENVIRONMENT</span><span class="st"> successful"</span></span>
<span id="cb12-64"><a href="#cb12-64" aria-hidden="true"></a></span>
<span id="cb12-65"><a href="#cb12-65" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"✓ Deployment successful"</span></span>
<span id="cb12-66"><a href="#cb12-66" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">"Log: </span><span class="va">$LOG_FILE</span><span class="st">"</span></span></code></pre>
</div>



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



<p>You now have a complete WordPress deployment pipeline built with WP-CLI and Bash.</p>



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



<p><strong>Week 1</strong>: Basic automation</p>



<ul class="wp-block-list">
<li>Create simple deploy scripts</li>



<li>Add backup mechanisms</li>



<li>Implement rollback procedures</li>
</ul>



<p><strong>Week 2</strong>: Testing integration</p>



<ul class="wp-block-list">
<li>Add validation checks</li>



<li>Create health checks</li>



<li>Automate testing</li>
</ul>



<p><strong>Week 3</strong>: Zero-downtime</p>



<ul class="wp-block-list">
<li>Implement symlink strategy</li>



<li>Set up blue-green deployment</li>



<li>Test switching mechanisms</li>
</ul>



<p><strong>Week 4</strong>: Production hardening</p>



<ul class="wp-block-list">
<li>Add monitoring</li>



<li>Implement notifications</li>



<li>Document procedures</li>
</ul>



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



<ol class="wp-block-list">
<li><strong><a href="#">Container Deployments</a></strong> &#8211; Docker-based pipelines</li>



<li><strong><a href="#">GitOps Workflows</a></strong> &#8211; Git-driven deployments</li>



<li><strong><a href="#">Multi-Region Deployment</a></strong> &#8211; Global site deployment</li>
</ol>



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



<p><strong><a href="#">Download deployment scripts</a></strong> including:</p>



<ul class="wp-block-list">
<li>Complete pipeline system</li>



<li>Testing frameworks</li>



<li>Rollback automation</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>DevOps best practices</li>



<li>Deployment strategies</li>
</ul>



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



<p>A WordPress deployment pipeline transforms risky manual deployments into reliable, automated workflows that enable rapid, confident releases to production.</p>



<p>What we covered:</p>



<p>✅ Deployment pipeline components and stages <br>✅ Basic deployment automation with backups <br>✅ Staging to production workflows <br>✅ Zero-downtime deployment strategies <br>✅ Database migration handling <br>✅ Complete production pipeline integration</p>



<p>Master these techniques, and you’ll deploy WordPress changes confidently dozens of times per day with zero downtime and instant rollback capability.</p>



<p><strong>Ready for more?</strong> Learn <a href="#">continuous integration</a> or <a href="#">infrastructure as code</a>.</p>



<p><strong>Questions about WordPress deployment pipelines?</strong> Drop a comment below!</p>



<p><strong>Found this helpful?</strong> Share with other WordPress DevOps teams.</p>
<p>The post <a href="https://wpclimastery.com/blog/build-a-wordpress-deployment-pipeline-with-wp-cli-and-bash/">Build a WordPress Deployment Pipeline with WP-CLI and Bash</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Bulk Update WordPress Plugins and Themes Across Multiple Sites with WP-CLI</title>
		<link>https://wpclimastery.com/blog/bulk-update-wordpress-plugins-and-themes-across-multiple-sites-with-wp-cli/</link>
		
		<dc:creator><![CDATA[Krasen]]></dc:creator>
		<pubDate>Wed, 10 Dec 2025 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Automation]]></category>
		<category><![CDATA[bulk wordpress updates]]></category>
		<category><![CDATA[update multiple wordpress sites]]></category>
		<category><![CDATA[wordpress maintenance automation]]></category>
		<category><![CDATA[wp-cli bulk update]]></category>
		<category><![CDATA[wpcli plugin updates]]></category>
		<guid isPermaLink="false">https://wpclimastery.com/?p=137</guid>

					<description><![CDATA[<p>Managing updates for 10, 50, or 100 WordPress sites means logging into each dashboard, clicking through update screens, and praying nothing breaks. It wastes hours every week and delays critical...</p>
<p>The post <a href="https://wpclimastery.com/blog/bulk-update-wordpress-plugins-and-themes-across-multiple-sites-with-wp-cli/">Bulk Update WordPress Plugins and Themes Across Multiple Sites with WP-CLI</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Managing updates for 10, 50, or 100 WordPress sites means logging into each dashboard, clicking through update screens, and praying nothing breaks. It wastes hours every week and delays critical security updates.</p>
<p>WP-CLI lets you update all plugins and themes across unlimited WordPress sites from your terminal—in minutes instead of hours. Add safety checks, automatic backups, and rollback capabilities, and you have enterprise-grade update automation.</p>
<p>In this guide, you’ll learn professional techniques for bulk updating WordPress installations safely, including pre-update backups, staged rollouts, and automated testing used by WordPress agencies managing hundreds of sites.</p>
<h3 id="why-bulk-update">Why Bulk Update with WP-CLI?</h3>
<p><a href="https://wordpress.org/support/article/updating-wordpress/">WordPress updates</a> are critical for security, but manual updates don’t scale.</p>
<h4 id="problems-with-manual-updates">Problems with Manual Updates</h4>
<p><strong>Time-consuming</strong>: Updating 50 sites manually takes 5-8 hours of repetitive clicking.</p>
<p><strong>Security delays</strong>: Critical security patches sit uninstalled for days or weeks.</p>
<p><strong>Inconsistent</strong>: Some sites get updated, others forgotten, creating security gaps.</p>
<p><strong>No rollback</strong>: If an update breaks a site, you’re manually restoring backups.</p>
<p><strong>Update fatigue</strong>: Developers avoid updates because the process is painful.</p>
<h4 id="wp-cli-bulk-update-advantages">WP-CLI Bulk Update Advantages</h4>
<p><strong>Fast</strong>: Update 100 sites in under 30 minutes with automation scripts.</p>
<p><strong>Consistent</strong>: Same update process across all sites prevents human error.</p>
<p><strong>Safe</strong>: Automated backups before every update enable instant rollback.</p>
<p><strong>Scheduled</strong>: Cron automation handles updates during maintenance windows.</p>
<p><strong>Testable</strong>: Stage updates on test sites before production rollout.</p>
<p>According to <a href="https://wpengine.com/resources/wordpress-security-report/">WordPress security research</a>, 60% of hacked WordPress sites were compromised due to outdated plugins. Efficient update workflows prevent breaches.</p>
<h3 id="single-site-updates">Single Site Update Operations</h3>
<p>Master updating individual WordPress installations first.</p>
<h4 id="update-all-plugins">Update All Plugins</h4>
<div class="sourceCode" id="cb1">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true"></a><span class="co"># Update all plugins</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true"></a><span class="ex">wp</span> plugin update --all</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true"></a><span class="co"># Preview updates without installing</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true"></a><span class="ex">wp</span> plugin update --all --dry-run</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true"></a><span class="co"># Update excluding specific plugins</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true"></a><span class="ex">wp</span> plugin update --all --exclude=woocommerce,elementor</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true"></a><span class="co"># Update specific plugins only</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true"></a><span class="ex">wp</span> plugin update wordfence yoast-seo contact-form-7</span></code></pre>
</div>
<h4 id="update-all-themes">Update All Themes</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"># Update all themes</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true"></a><span class="ex">wp</span> theme update --all</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"># Update specific theme</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true"></a><span class="ex">wp</span> theme update astra</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"># Dry run to preview</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true"></a><span class="ex">wp</span> theme update --all --dry-run</span></code></pre>
</div>
<h4 id="update-wordpress-core">Update WordPress Core</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"># Update to latest version</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true"></a><span class="ex">wp</span> core update</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"># Update to specific version</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true"></a><span class="ex">wp</span> core update --version=6.4.2</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true"></a></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true"></a><span class="co"># Update minor versions only (security updates)</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true"></a><span class="ex">wp</span> core update --minor</span></code></pre>
</div>
<p><strong>Pro Tip</strong>: Always run <code>--dry-run</code> first to preview what will be updated before making changes.</p>
<p>Learn more in the <a href="https://developer.wordpress.org/cli/commands/plugin/update/">official WP-CLI plugin update documentation</a>.</p>
<h3 id="safe-updates">Safe Update Workflow with Backups</h3>
<p>Never update without backups and rollback capability.</p>
<h4 id="complete-backup-before-updates">Complete Backup Before Updates</h4>
<div class="sourceCode" id="cb4">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a><span class="co"># safe-update.sh - Update with automatic backup</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="va">SITE_PATH=</span><span class="st">&quot;/var/www/html&quot;</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a><span class="va">BACKUP_DIR=</span><span class="st">&quot;/backups&quot;</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a><span class="va">DATE=$(</span><span class="fu">date</span> +%Y%m%d_%H%M%S<span class="va">)</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true"></a><span class="bu">cd</span> <span class="st">&quot;</span><span class="va">$SITE_PATH</span><span class="st">&quot;</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true"></a></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;Creating pre-update backup...&quot;</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true"></a></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true"></a><span class="co"># Backup database</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true"></a><span class="ex">wp</span> db export <span class="st">&quot;</span><span class="va">$BACKUP_DIR</span><span class="st">/db-before-update-</span><span class="va">$DATE</span><span class="st">.sql.gz&quot;</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="co"># Backup files (plugins, themes, uploads)</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true"></a><span class="fu">tar</span> -czf <span class="st">&quot;</span><span class="va">$BACKUP_DIR</span><span class="st">/files-before-update-</span><span class="va">$DATE</span><span class="st">.tar.gz&quot;</span> <span class="kw">\</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true"></a>    <span class="ex">wp-content/plugins</span> <span class="kw">\</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true"></a>    <span class="ex">wp-content/themes</span> <span class="kw">\</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true"></a>    <span class="ex">wp-content/uploads</span></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true"></a></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;✓ Backup complete&quot;</span></span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true"></a></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true"></a><span class="co"># Update plugins</span></span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;Updating plugins...&quot;</span></span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true"></a><span class="ex">wp</span> plugin update --all</span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true"></a></span>
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true"></a><span class="co"># Update themes</span></span>
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;Updating themes...&quot;</span></span>
<span id="cb4-29"><a href="#cb4-29" aria-hidden="true"></a><span class="ex">wp</span> theme update --all</span>
<span id="cb4-30"><a href="#cb4-30" aria-hidden="true"></a></span>
<span id="cb4-31"><a href="#cb4-31" aria-hidden="true"></a><span class="co"># Verify WordPress still works</span></span>
<span id="cb4-32"><a href="#cb4-32" aria-hidden="true"></a><span class="kw">if</span> <span class="ex">wp</span> core is-installed<span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb4-33"><a href="#cb4-33" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;✓ Updates successful, WordPress functional&quot;</span></span>
<span id="cb4-34"><a href="#cb4-34" aria-hidden="true"></a><span class="kw">else</span></span>
<span id="cb4-35"><a href="#cb4-35" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;✗ WordPress check failed, restoring backup...&quot;</span></span>
<span id="cb4-36"><a href="#cb4-36" aria-hidden="true"></a>    <span class="ex">wp</span> db import <span class="st">&quot;</span><span class="va">$BACKUP_DIR</span><span class="st">/db-before-update-</span><span class="va">$DATE</span><span class="st">.sql.gz&quot;</span></span>
<span id="cb4-37"><a href="#cb4-37" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb4-38"><a href="#cb4-38" aria-hidden="true"></a><span class="kw">fi</span></span></code></pre>
</div>
<h4 id="rollback-failed-updates">Rollback Failed Updates</h4>
<div class="sourceCode" id="cb5">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true"></a><span class="co"># rollback-update.sh - Restore last backup</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true"></a><span class="va">BACKUP_DIR=</span><span class="st">&quot;/backups&quot;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true"></a><span class="co"># Find most recent backup</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true"></a><span class="va">LATEST_DB=$(</span><span class="fu">ls</span> -t <span class="st">&quot;</span><span class="va">$BACKUP_DIR</span><span class="st">&quot;</span>/db-before-update-*.sql.gz <span class="kw">|</span> <span class="fu">head</span> -1<span class="va">)</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true"></a><span class="va">LATEST_FILES=$(</span><span class="fu">ls</span> -t <span class="st">&quot;</span><span class="va">$BACKUP_DIR</span><span class="st">&quot;</span>/files-before-update-*.tar.gz <span class="kw">|</span> <span class="fu">head</span> -1<span class="va">)</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true"></a></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true"></a><span class="kw">if</span><span class="bu"> [</span> <span class="ot">-z</span> <span class="st">&quot;</span><span class="va">$LATEST_DB</span><span class="st">&quot;</span><span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;No backup found!&quot;</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true"></a>    <span class="bu">exit</span> 1</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true"></a></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;Rolling back to backup: </span><span class="va">$LATEST_DB</span><span class="st">&quot;</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true"></a></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true"></a><span class="co"># Restore database</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true"></a><span class="ex">wp</span> db import <span class="st">&quot;</span><span class="va">$LATEST_DB</span><span class="st">&quot;</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true"></a></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true"></a><span class="co"># Restore files</span></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true"></a><span class="fu">tar</span> -xzf <span class="st">&quot;</span><span class="va">$LATEST_FILES</span><span class="st">&quot;</span> -C /var/www/html/</span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true"></a></span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;✓ Rollback complete&quot;</span></span></code></pre>
</div>
<h3 id="bulk-updates">Bulk Update Across Multiple Sites</h3>
<p>Update plugins and themes across all WordPress installations you manage.</p>
<h4 id="update-multiple-sites-script">Update Multiple Sites Script</h4>
<div class="sourceCode" id="cb6">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true"></a><span class="co"># bulk-update-sites.sh - Update all managed sites</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true"></a></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true"></a><span class="va">SITES=(</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true"></a>    <span class="st">&quot;/var/www/site1&quot;</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true"></a>    <span class="st">&quot;/var/www/site2&quot;</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true"></a>    <span class="st">&quot;/var/www/site3&quot;</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true"></a>    <span class="st">&quot;/var/www/client-site&quot;</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true"></a>)</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true"></a></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true"></a><span class="va">BACKUP_DIR=</span><span class="st">&quot;/backups&quot;</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true"></a><span class="va">LOG_FILE=</span><span class="st">&quot;/var/log/wp-bulk-updates.log&quot;</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true"></a></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true"></a><span class="fu">log()</span> <span class="kw">{</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;[</span><span class="va">$(</span><span class="fu">date</span> <span class="st">&#39;+%Y-%m-%d %H:%M:%S&#39;</span><span class="va">)</span><span class="st">] </span><span class="va">$@</span><span class="st">&quot;</span> <span class="kw">|</span> <span class="fu">tee</span> -a <span class="st">&quot;</span><span class="va">$LOG_FILE</span><span class="st">&quot;</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true"></a><span class="kw">}</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true"></a></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true"></a><span class="ex">log</span> <span class="st">&quot;=== Bulk WordPress Update Started ===&quot;</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true"></a></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true"></a><span class="kw">for</span> <span class="ex">SITE</span> in <span class="st">&quot;</span><span class="va">${SITES[@]}</span><span class="st">&quot;</span><span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true"></a>    <span class="va">SITE_NAME=$(</span><span class="fu">basename</span> <span class="st">&quot;</span><span class="va">$SITE</span><span class="st">&quot;</span><span class="va">)</span></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true"></a>    <span class="ex">log</span> <span class="st">&quot;Processing: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true"></a></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true"></a>    <span class="bu">cd</span> <span class="st">&quot;</span><span class="va">$SITE</span><span class="st">&quot;</span> <span class="kw">||</span> <span class="kw">{</span> <span class="ex">log</span> <span class="st">&quot;ERROR: Cannot access </span><span class="va">$SITE</span><span class="st">&quot;</span><span class="kw">;</span> <span class="bu">continue</span><span class="kw">;</span> <span class="kw">}</span></span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true"></a></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true"></a>    <span class="co"># Check WordPress is installed</span></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true"></a>    <span class="kw">if</span> ! <span class="ex">wp</span> core is-installed <span class="op">2&gt;</span>/dev/null<span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true"></a>        <span class="ex">log</span> <span class="st">&quot;WARNING: WordPress not found in </span><span class="va">$SITE</span><span class="st">, skipping&quot;</span></span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true"></a>        <span class="bu">continue</span></span>
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb6-31"><a href="#cb6-31" aria-hidden="true"></a></span>
<span id="cb6-32"><a href="#cb6-32" aria-hidden="true"></a>    <span class="co"># Backup database</span></span>
<span id="cb6-33"><a href="#cb6-33" aria-hidden="true"></a>    <span class="va">BACKUP_FILE=</span><span class="st">&quot;</span><span class="va">$BACKUP_DIR</span><span class="st">/</span><span class="va">${SITE_NAME}</span><span class="st">-</span><span class="va">$(</span><span class="fu">date</span> +%Y%m%d<span class="va">)</span><span class="st">.sql.gz&quot;</span></span>
<span id="cb6-34"><a href="#cb6-34" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ex">wp</span> db export <span class="st">&quot;</span><span class="va">$BACKUP_FILE</span><span class="st">&quot;</span><span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb6-35"><a href="#cb6-35" aria-hidden="true"></a>        <span class="ex">log</span> <span class="st">&quot;✓ Backed up: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb6-36"><a href="#cb6-36" aria-hidden="true"></a>    <span class="kw">else</span></span>
<span id="cb6-37"><a href="#cb6-37" aria-hidden="true"></a>        <span class="ex">log</span> <span class="st">&quot;✗ Backup failed for </span><span class="va">$SITE_NAME</span><span class="st">, skipping updates&quot;</span></span>
<span id="cb6-38"><a href="#cb6-38" aria-hidden="true"></a>        <span class="bu">continue</span></span>
<span id="cb6-39"><a href="#cb6-39" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb6-40"><a href="#cb6-40" aria-hidden="true"></a></span>
<span id="cb6-41"><a href="#cb6-41" aria-hidden="true"></a>    <span class="co"># Update plugins</span></span>
<span id="cb6-42"><a href="#cb6-42" aria-hidden="true"></a>    <span class="ex">log</span> <span class="st">&quot;Updating plugins for </span><span class="va">$SITE_NAME</span><span class="st">...&quot;</span></span>
<span id="cb6-43"><a href="#cb6-43" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ex">wp</span> plugin update --all --quiet<span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb6-44"><a href="#cb6-44" aria-hidden="true"></a>        <span class="ex">log</span> <span class="st">&quot;✓ Plugins updated: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb6-45"><a href="#cb6-45" aria-hidden="true"></a>    <span class="kw">else</span></span>
<span id="cb6-46"><a href="#cb6-46" aria-hidden="true"></a>        <span class="ex">log</span> <span class="st">&quot;✗ Plugin update failed: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb6-47"><a href="#cb6-47" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb6-48"><a href="#cb6-48" aria-hidden="true"></a></span>
<span id="cb6-49"><a href="#cb6-49" aria-hidden="true"></a>    <span class="co"># Update themes</span></span>
<span id="cb6-50"><a href="#cb6-50" aria-hidden="true"></a>    <span class="ex">log</span> <span class="st">&quot;Updating themes for </span><span class="va">$SITE_NAME</span><span class="st">...&quot;</span></span>
<span id="cb6-51"><a href="#cb6-51" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ex">wp</span> theme update --all --quiet<span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb6-52"><a href="#cb6-52" aria-hidden="true"></a>        <span class="ex">log</span> <span class="st">&quot;✓ Themes updated: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb6-53"><a href="#cb6-53" aria-hidden="true"></a>    <span class="kw">else</span></span>
<span id="cb6-54"><a href="#cb6-54" aria-hidden="true"></a>        <span class="ex">log</span> <span class="st">&quot;✗ Theme update failed: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb6-55"><a href="#cb6-55" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb6-56"><a href="#cb6-56" aria-hidden="true"></a></span>
<span id="cb6-57"><a href="#cb6-57" aria-hidden="true"></a>    <span class="ex">log</span> <span class="st">&quot;---&quot;</span></span>
<span id="cb6-58"><a href="#cb6-58" aria-hidden="true"></a><span class="kw">done</span></span>
<span id="cb6-59"><a href="#cb6-59" aria-hidden="true"></a></span>
<span id="cb6-60"><a href="#cb6-60" aria-hidden="true"></a><span class="ex">log</span> <span class="st">&quot;=== Bulk Update Complete ===&quot;</span></span></code></pre>
</div>
<p><strong>Use Case</strong>: Agencies managing dozens of client sites can update all installations in one maintenance window.</p>
<h4 id="update-with-email-notifications">Update with Email Notifications</h4>
<div class="sourceCode" id="cb7">
<pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true"></a><span class="co">#!/bin/bash</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true"></a><span class="co"># bulk-update-notify.sh - Updates with email reporting</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true"></a></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true"></a><span class="va">ADMIN_EMAIL=</span><span class="st">&quot;admin@example.com&quot;</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true"></a><span class="va">REPORT=</span><span class="st">&quot;/tmp/update-report-</span><span class="va">$$</span><span class="st">.txt&quot;</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true"></a></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true"></a><span class="kw">{</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;WordPress Bulk Update Report&quot;</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;Generated: </span><span class="va">$(</span><span class="fu">date</span><span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;================================&quot;</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;&quot;</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true"></a></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true"></a>    <span class="co"># Update logic here (from previous script)</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true"></a>    <span class="co"># ...</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true"></a></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true"></a><span class="kw">}</span> <span class="op">&gt;</span> <span class="st">&quot;</span><span class="va">$REPORT</span><span class="st">&quot;</span></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true"></a></span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true"></a><span class="co"># Email report</span></span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true"></a><span class="ex">mail</span> -s <span class="st">&quot;WordPress Bulk Update Report&quot;</span> <span class="st">&quot;</span><span class="va">$ADMIN_EMAIL</span><span class="st">&quot;</span> <span class="op">&lt;</span> <span class="st">&quot;</span><span class="va">$REPORT</span><span class="st">&quot;</span></span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true"></a></span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true"></a><span class="fu">rm</span> <span class="st">&quot;</span><span class="va">$REPORT</span><span class="st">&quot;</span></span></code></pre>
</div>
<h3 id="selective-updates">Selective and Staged Update Strategies</h3>
<p>Control exactly what gets updated and when.</p>
<h4 id="update-specific-plugins-only">Update Specific Plugins Only</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"># update-security-plugins.sh - Update security plugins only</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="va">SECURITY_PLUGINS=(</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true"></a>    <span class="st">&quot;wordfence&quot;</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true"></a>    <span class="st">&quot;sucuri-scanner&quot;</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true"></a>    <span class="st">&quot;ithemes-security&quot;</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true"></a>    <span class="st">&quot;all-in-one-wp-security-and-firewall&quot;</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true"></a>)</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="kw">for</span> <span class="ex">PLUGIN</span> in <span class="st">&quot;</span><span class="va">${SECURITY_PLUGINS[@]}</span><span class="st">&quot;</span><span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;Updating security plugin: </span><span class="va">$PLUGIN</span><span class="st">&quot;</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true"></a>    <span class="ex">wp</span> plugin update <span class="st">&quot;</span><span class="va">$PLUGIN</span><span class="st">&quot;</span> <span class="op">2&gt;</span>/dev/null</span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true"></a><span class="kw">done</span></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true"></a></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;✓ Security plugins updated&quot;</span></span></code></pre>
</div>
<h4 id="staged-rollout-strategy">Staged Rollout Strategy</h4>
<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="co">#!/bin/bash</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true"></a><span class="co"># staged-update.sh - Update test sites first, then production</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true"></a></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true"></a><span class="va">TEST_SITES=(</span><span class="st">&quot;/var/www/test1&quot;</span> <span class="st">&quot;/var/www/staging&quot;</span><span class="va">)</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true"></a><span class="va">PROD_SITES=(</span><span class="st">&quot;/var/www/prod1&quot;</span> <span class="st">&quot;/var/www/prod2&quot;</span><span class="va">)</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true"></a></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;Stage 1: Updating test sites...&quot;</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true"></a><span class="kw">for</span> <span class="ex">SITE</span> in <span class="st">&quot;</span><span class="va">${TEST_SITES[@]}</span><span class="st">&quot;</span><span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true"></a>    <span class="bu">cd</span> <span class="st">&quot;</span><span class="va">$SITE</span><span class="st">&quot;</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true"></a>    <span class="ex">wp</span> plugin update --all</span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true"></a>    <span class="ex">wp</span> theme update --all</span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true"></a><span class="kw">done</span></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true"></a></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;Test sites updated. Monitor for 24 hours before production.&quot;</span></span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true"></a><span class="bu">read</span> -p <span class="st">&quot;Proceed with production updates? (y/n) &quot;</span> -n 1 -r</span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true"></a><span class="bu">echo</span></span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true"></a></span>
<span id="cb9-18"><a href="#cb9-18" 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="cb9-19"><a href="#cb9-19" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;Production updates cancelled&quot;</span></span>
<span id="cb9-20"><a href="#cb9-20" aria-hidden="true"></a>    <span class="bu">exit</span> 0</span>
<span id="cb9-21"><a href="#cb9-21" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb9-22"><a href="#cb9-22" aria-hidden="true"></a></span>
<span id="cb9-23"><a href="#cb9-23" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;Stage 2: Updating production sites...&quot;</span></span>
<span id="cb9-24"><a href="#cb9-24" aria-hidden="true"></a><span class="kw">for</span> <span class="ex">SITE</span> in <span class="st">&quot;</span><span class="va">${PROD_SITES[@]}</span><span class="st">&quot;</span><span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb9-25"><a href="#cb9-25" aria-hidden="true"></a>    <span class="bu">cd</span> <span class="st">&quot;</span><span class="va">$SITE</span><span class="st">&quot;</span></span>
<span id="cb9-26"><a href="#cb9-26" aria-hidden="true"></a>    <span class="co"># Backup first</span></span>
<span id="cb9-27"><a href="#cb9-27" aria-hidden="true"></a>    <span class="ex">wp</span> db export <span class="st">&quot;/backups/</span><span class="va">$(</span><span class="fu">basename</span> <span class="va">$SITE)</span><span class="st">-prod-</span><span class="va">$(</span><span class="fu">date</span> +%Y%m%d<span class="va">)</span><span class="st">.sql.gz&quot;</span></span>
<span id="cb9-28"><a href="#cb9-28" aria-hidden="true"></a>    <span class="ex">wp</span> plugin update --all</span>
<span id="cb9-29"><a href="#cb9-29" aria-hidden="true"></a>    <span class="ex">wp</span> theme update --all</span>
<span id="cb9-30"><a href="#cb9-30" aria-hidden="true"></a><span class="kw">done</span></span>
<span id="cb9-31"><a href="#cb9-31" aria-hidden="true"></a></span>
<span id="cb9-32"><a href="#cb9-32" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;✓ Staged rollout complete&quot;</span></span></code></pre>
</div>
<p>Learn about <a href="https://wordpress.org/support/article/wordpress-features/#staging-sites">WordPress staging environments</a> best practices.</p>
<h4 id="exclude-problematic-plugins">Exclude Problematic Plugins</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">#!/bin/bash</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true"></a><span class="co"># update-exclude-known-issues.sh</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true"></a></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true"></a><span class="co"># Plugins known to have issues in latest version</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true"></a><span class="va">EXCLUDE_PLUGINS=(</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true"></a>    <span class="st">&quot;broken-plugin&quot;</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true"></a>    <span class="st">&quot;problematic-theme-plugin&quot;</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true"></a>)</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true"></a></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true"></a><span class="co"># Build exclude parameter</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true"></a><span class="va">EXCLUDE_PARAM=</span><span class="st">&quot;&quot;</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true"></a><span class="kw">for</span> <span class="ex">PLUGIN</span> in <span class="st">&quot;</span><span class="va">${EXCLUDE_PLUGINS[@]}</span><span class="st">&quot;</span><span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true"></a>    <span class="va">EXCLUDE_PARAM=</span><span class="st">&quot;</span><span class="va">${EXCLUDE_PARAM}${PLUGIN}</span><span class="st">,&quot;</span></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true"></a><span class="kw">done</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true"></a></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true"></a><span class="co"># Remove trailing comma</span></span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true"></a><span class="va">EXCLUDE_PARAM=${EXCLUDE_PARAM%</span>,<span class="va">}</span></span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true"></a></span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true"></a><span class="co"># Update all except excluded</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true"></a><span class="ex">wp</span> plugin update --all --exclude=<span class="st">&quot;</span><span class="va">$EXCLUDE_PARAM</span><span class="st">&quot;</span></span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true"></a></span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;Updated all plugins except: </span><span class="va">$EXCLUDE_PARAM</span><span class="st">&quot;</span></span></code></pre>
</div>
<h3 id="automated-updates">Automated Update Scheduling</h3>
<p>Schedule bulk updates to run automatically during maintenance windows.</p>
<h4 id="weekly-automated-updates">Weekly Automated Updates</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"># weekly-auto-update.sh - Run via cron</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">LOG_FILE=</span><span class="st">&quot;/var/log/auto-updates.log&quot;</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true"></a><span class="va">BACKUP_DIR=</span><span class="st">&quot;/backups/auto&quot;</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true"></a><span class="va">SITES_DIR=</span><span class="st">&quot;/var/www&quot;</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true"></a></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true"></a><span class="fu">log()</span> <span class="kw">{</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;[</span><span class="va">$(</span><span class="fu">date</span> <span class="st">&#39;+%Y-%m-%d %H:%M:%S&#39;</span><span class="va">)</span><span class="st">] </span><span class="va">$@</span><span class="st">&quot;</span> <span class="kw">|</span> <span class="fu">tee</span> -a <span class="st">&quot;</span><span class="va">$LOG_FILE</span><span class="st">&quot;</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true"></a><span class="kw">}</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="ex">log</span> <span class="st">&quot;=== Automated Weekly Update Started ===&quot;</span></span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true"></a></span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true"></a><span class="co"># Find all WordPress installations</span></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true"></a><span class="fu">find</span> <span class="st">&quot;</span><span class="va">$SITES_DIR</span><span class="st">&quot;</span> -maxdepth 2 -name <span class="st">&quot;wp-config.php&quot;</span> <span class="kw">|</span> <span class="kw">while</span> <span class="bu">read</span> <span class="va">WP_CONFIG</span>; <span class="kw">do</span></span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true"></a>    <span class="va">SITE_PATH=$(</span><span class="fu">dirname</span> <span class="st">&quot;</span><span class="va">$WP_CONFIG</span><span class="st">&quot;</span><span class="va">)</span></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true"></a>    <span class="va">SITE_NAME=$(</span><span class="fu">basename</span> <span class="st">&quot;</span><span class="va">$SITE_PATH</span><span class="st">&quot;</span><span class="va">)</span></span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true"></a></span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true"></a>    <span class="bu">cd</span> <span class="st">&quot;</span><span class="va">$SITE_PATH</span><span class="st">&quot;</span></span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true"></a>    <span class="ex">log</span> <span class="st">&quot;Updating: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true"></a></span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true"></a>    <span class="co"># Backup</span></span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true"></a>    <span class="fu">mkdir</span> -p <span class="st">&quot;</span><span class="va">$BACKUP_DIR</span><span class="st">&quot;</span></span>
<span id="cb11-24"><a href="#cb11-24" aria-hidden="true"></a>    <span class="ex">wp</span> db export <span class="st">&quot;</span><span class="va">$BACKUP_DIR</span><span class="st">/</span><span class="va">$SITE_NAME</span><span class="st">-</span><span class="va">$(</span><span class="fu">date</span> +%Y%m%d<span class="va">)</span><span class="st">.sql.gz&quot;</span> --quiet</span>
<span id="cb11-25"><a href="#cb11-25" aria-hidden="true"></a></span>
<span id="cb11-26"><a href="#cb11-26" aria-hidden="true"></a>    <span class="co"># Update</span></span>
<span id="cb11-27"><a href="#cb11-27" aria-hidden="true"></a>    <span class="ex">wp</span> plugin update --all --quiet</span>
<span id="cb11-28"><a href="#cb11-28" aria-hidden="true"></a>    <span class="ex">wp</span> theme update --all --quiet</span>
<span id="cb11-29"><a href="#cb11-29" aria-hidden="true"></a>    <span class="ex">wp</span> core update --minor --quiet</span>
<span id="cb11-30"><a href="#cb11-30" aria-hidden="true"></a></span>
<span id="cb11-31"><a href="#cb11-31" aria-hidden="true"></a>    <span class="ex">log</span> <span class="st">&quot;✓ Updated: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb11-32"><a href="#cb11-32" aria-hidden="true"></a><span class="kw">done</span></span>
<span id="cb11-33"><a href="#cb11-33" aria-hidden="true"></a></span>
<span id="cb11-34"><a href="#cb11-34" aria-hidden="true"></a><span class="co"># Cleanup old backups (keep 30 days)</span></span>
<span id="cb11-35"><a href="#cb11-35" aria-hidden="true"></a><span class="fu">find</span> <span class="st">&quot;</span><span class="va">$BACKUP_DIR</span><span class="st">&quot;</span> -name <span class="st">&quot;*.sql.gz&quot;</span> -mtime +30 -delete</span>
<span id="cb11-36"><a href="#cb11-36" aria-hidden="true"></a></span>
<span id="cb11-37"><a href="#cb11-37" aria-hidden="true"></a><span class="ex">log</span> <span class="st">&quot;=== Automated Update Complete ===&quot;</span></span></code></pre>
</div>
<p>Schedule with cron:</p>
<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"># Run every Sunday at 2 AM</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true"></a><span class="ex">0</span> 2 * * 0 /usr/local/bin/weekly-auto-update.sh</span></code></pre>
</div>
<h4 id="update-monitoring-and-alerts">Update Monitoring and Alerts</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"># monitor-updates.sh - Check for available updates</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="va">SITES=(</span><span class="st">&quot;/var/www/site1&quot;</span> <span class="st">&quot;/var/www/site2&quot;</span><span class="va">)</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true"></a><span class="va">ALERT_EMAIL=</span><span class="st">&quot;admin@example.com&quot;</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true"></a><span class="va">UPDATES_FOUND=</span>0</span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true"></a></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true"></a><span class="va">REPORT=</span><span class="st">&quot;/tmp/update-status-</span><span class="va">$$</span><span class="st">.txt&quot;</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="kw">{</span></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;WordPress Update Status Report&quot;</span></span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;Generated: </span><span class="va">$(</span><span class="fu">date</span><span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;==============================&quot;</span></span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true"></a>    <span class="bu">echo</span> <span class="st">&quot;&quot;</span></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="kw">for</span> <span class="ex">SITE</span> in <span class="st">&quot;</span><span class="va">${SITES[@]}</span><span class="st">&quot;</span><span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true"></a>        <span class="va">SITE_NAME=$(</span><span class="fu">basename</span> <span class="st">&quot;</span><span class="va">$SITE</span><span class="st">&quot;</span><span class="va">)</span></span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true"></a>        <span class="bu">cd</span> <span class="st">&quot;</span><span class="va">$SITE</span><span class="st">&quot;</span></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="va">PLUGIN_UPDATES=$(</span><span class="ex">wp</span> plugin list --update=available --format=count <span class="op">2&gt;</span>/dev/null<span class="va">)</span></span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true"></a>        <span class="va">THEME_UPDATES=$(</span><span class="ex">wp</span> theme list --update=available --format=count <span class="op">2&gt;</span>/dev/null<span class="va">)</span></span>
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true"></a></span>
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true"></a>        <span class="kw">if</span><span class="bu"> [</span> <span class="st">&quot;</span><span class="va">$PLUGIN_UPDATES</span><span class="st">&quot;</span> <span class="ot">-gt</span> 0<span class="bu"> ]</span> <span class="kw">||</span><span class="bu"> [</span> <span class="st">&quot;</span><span class="va">$THEME_UPDATES</span><span class="st">&quot;</span> <span class="ot">-gt</span> 0<span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true"></a>            <span class="bu">echo</span> <span class="st">&quot;Site: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true"></a>            <span class="bu">echo</span> <span class="st">&quot;  Plugins needing update: </span><span class="va">$PLUGIN_UPDATES</span><span class="st">&quot;</span></span>
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true"></a>            <span class="bu">echo</span> <span class="st">&quot;  Themes needing update: </span><span class="va">$THEME_UPDATES</span><span class="st">&quot;</span></span>
<span id="cb13-27"><a href="#cb13-27" aria-hidden="true"></a>            <span class="bu">echo</span> <span class="st">&quot;&quot;</span></span>
<span id="cb13-28"><a href="#cb13-28" aria-hidden="true"></a>            <span class="va">UPDATES_FOUND=$((</span>UPDATES_FOUND + 1<span class="va">))</span></span>
<span id="cb13-29"><a href="#cb13-29" aria-hidden="true"></a>        <span class="kw">fi</span></span>
<span id="cb13-30"><a href="#cb13-30" aria-hidden="true"></a>    <span class="kw">done</span></span>
<span id="cb13-31"><a href="#cb13-31" aria-hidden="true"></a></span>
<span id="cb13-32"><a href="#cb13-32" aria-hidden="true"></a>    <span class="kw">if</span><span class="bu"> [</span> <span class="st">&quot;</span><span class="va">$UPDATES_FOUND</span><span class="st">&quot;</span> <span class="ot">-eq</span> 0<span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb13-33"><a href="#cb13-33" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">&quot;All sites are up to date!&quot;</span></span>
<span id="cb13-34"><a href="#cb13-34" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb13-35"><a href="#cb13-35" aria-hidden="true"></a></span>
<span id="cb13-36"><a href="#cb13-36" aria-hidden="true"></a><span class="kw">}</span> <span class="op">&gt;</span> <span class="st">&quot;</span><span class="va">$REPORT</span><span class="st">&quot;</span></span>
<span id="cb13-37"><a href="#cb13-37" aria-hidden="true"></a></span>
<span id="cb13-38"><a href="#cb13-38" aria-hidden="true"></a><span class="co"># Send email if updates available</span></span>
<span id="cb13-39"><a href="#cb13-39" aria-hidden="true"></a><span class="kw">if</span><span class="bu"> [</span> <span class="st">&quot;</span><span class="va">$UPDATES_FOUND</span><span class="st">&quot;</span> <span class="ot">-gt</span> 0<span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb13-40"><a href="#cb13-40" aria-hidden="true"></a>    <span class="ex">mail</span> -s <span class="st">&quot;WordPress Updates Available (</span><span class="va">$UPDATES_FOUND</span><span class="st"> sites)&quot;</span> <span class="st">&quot;</span><span class="va">$ALERT_EMAIL</span><span class="st">&quot;</span> <span class="op">&lt;</span> <span class="st">&quot;</span><span class="va">$REPORT</span><span class="st">&quot;</span></span>
<span id="cb13-41"><a href="#cb13-41" aria-hidden="true"></a><span class="kw">fi</span></span>
<span id="cb13-42"><a href="#cb13-42" aria-hidden="true"></a></span>
<span id="cb13-43"><a href="#cb13-43" aria-hidden="true"></a><span class="fu">rm</span> <span class="st">&quot;</span><span class="va">$REPORT</span><span class="st">&quot;</span></span></code></pre>
</div>
<h3 id="testing-verification">Testing and Verification</h3>
<p>Verify updates didn’t break functionality.</p>
<h4 id="post-update-health-check">Post-Update Health Check</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">#!/bin/bash</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true"></a><span class="co"># verify-updates.sh - Check sites after updates</span></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="va">SITES=(</span><span class="st">&quot;/var/www/site1&quot;</span> <span class="st">&quot;/var/www/site2&quot;</span><span class="va">)</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true"></a></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true"></a><span class="kw">for</span> <span class="ex">SITE</span> in <span class="st">&quot;</span><span class="va">${SITES[@]}</span><span class="st">&quot;</span><span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true"></a>    <span class="va">SITE_NAME=$(</span><span class="fu">basename</span> <span class="st">&quot;</span><span class="va">$SITE</span><span class="st">&quot;</span><span class="va">)</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true"></a>    <span class="bu">cd</span> <span class="st">&quot;</span><span class="va">$SITE</span><span class="st">&quot;</span></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="bu">echo</span> <span class="st">&quot;Checking: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true"></a></span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true"></a>    <span class="co"># Check WordPress is functional</span></span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true"></a>    <span class="kw">if</span> ! <span class="ex">wp</span> core is-installed <span class="op">2&gt;</span>/dev/null<span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">&quot;✗ WordPress check failed: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true"></a>        <span class="bu">continue</span></span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true"></a></span>
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true"></a>    <span class="co"># Check database connection</span></span>
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true"></a>    <span class="kw">if</span> ! <span class="ex">wp</span> db check <span class="op">2&gt;</span>/dev/null<span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">&quot;✗ Database check failed: </span><span class="va">$SITE_NAME</span><span class="st">&quot;</span></span>
<span id="cb14-21"><a href="#cb14-21" aria-hidden="true"></a>        <span class="bu">continue</span></span>
<span id="cb14-22"><a href="#cb14-22" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb14-23"><a href="#cb14-23" aria-hidden="true"></a></span>
<span id="cb14-24"><a href="#cb14-24" aria-hidden="true"></a>    <span class="co"># Check site responds</span></span>
<span id="cb14-25"><a href="#cb14-25" aria-hidden="true"></a>    <span class="va">SITE_URL=$(</span><span class="ex">wp</span> option get siteurl <span class="op">2&gt;</span>/dev/null<span class="va">)</span></span>
<span id="cb14-26"><a href="#cb14-26" aria-hidden="true"></a>    <span class="kw">if</span> <span class="ex">curl</span> -f -s <span class="st">&quot;</span><span class="va">$SITE_URL</span><span class="st">&quot;</span> <span class="op">&gt;</span> /dev/null<span class="kw">;</span> <span class="kw">then</span></span>
<span id="cb14-27"><a href="#cb14-27" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">&quot;✓ </span><span class="va">$SITE_NAME</span><span class="st"> is healthy&quot;</span></span>
<span id="cb14-28"><a href="#cb14-28" aria-hidden="true"></a>    <span class="kw">else</span></span>
<span id="cb14-29"><a href="#cb14-29" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">&quot;✗ </span><span class="va">$SITE_NAME</span><span class="st"> not responding&quot;</span></span>
<span id="cb14-30"><a href="#cb14-30" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb14-31"><a href="#cb14-31" aria-hidden="true"></a><span class="kw">done</span></span></code></pre>
</div>
<h4 id="automated-testing-script">Automated Testing Script</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">#!/bin/bash</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true"></a><span class="co"># test-after-update.sh</span></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"># Critical pages to test</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true"></a><span class="va">TEST_URLS=(</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true"></a>    <span class="st">&quot;/&quot;</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true"></a>    <span class="st">&quot;/shop&quot;</span></span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true"></a>    <span class="st">&quot;/checkout&quot;</span></span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true"></a>    <span class="st">&quot;/my-account&quot;</span></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true"></a>)</span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true"></a></span>
<span id="cb15-12"><a href="#cb15-12" aria-hidden="true"></a><span class="va">SITE_URL=$(</span><span class="ex">wp</span> option get siteurl<span class="va">)</span></span>
<span id="cb15-13"><a href="#cb15-13" aria-hidden="true"></a></span>
<span id="cb15-14"><a href="#cb15-14" aria-hidden="true"></a><span class="bu">echo</span> <span class="st">&quot;Testing critical pages...&quot;</span></span>
<span id="cb15-15"><a href="#cb15-15" aria-hidden="true"></a></span>
<span id="cb15-16"><a href="#cb15-16" aria-hidden="true"></a><span class="kw">for</span> <span class="ex">PATH</span> in <span class="st">&quot;</span><span class="va">${TEST_URLS[@]}</span><span class="st">&quot;</span><span class="kw">;</span> <span class="kw">do</span></span>
<span id="cb15-17"><a href="#cb15-17" aria-hidden="true"></a>    <span class="va">URL=</span><span class="st">&quot;</span><span class="va">${SITE_URL}${PATH}</span><span class="st">&quot;</span></span>
<span id="cb15-18"><a href="#cb15-18" aria-hidden="true"></a></span>
<span id="cb15-19"><a href="#cb15-19" aria-hidden="true"></a>    <span class="va">HTTP_CODE=$(</span><span class="ex">curl</span> -s -o /dev/null -w <span class="st">&quot;%{http_code}&quot;</span> <span class="st">&quot;</span><span class="va">$URL</span><span class="st">&quot;</span><span class="va">)</span></span>
<span id="cb15-20"><a href="#cb15-20" aria-hidden="true"></a></span>
<span id="cb15-21"><a href="#cb15-21" aria-hidden="true"></a>    <span class="kw">if</span><span class="bu"> [</span> <span class="st">&quot;</span><span class="va">$HTTP_CODE</span><span class="st">&quot;</span> <span class="ot">-eq</span> 200<span class="bu"> ]</span>; <span class="kw">then</span></span>
<span id="cb15-22"><a href="#cb15-22" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">&quot;✓ </span><span class="va">$PATH</span><span class="st"> - OK (200)&quot;</span></span>
<span id="cb15-23"><a href="#cb15-23" aria-hidden="true"></a>    <span class="kw">else</span></span>
<span id="cb15-24"><a href="#cb15-24" aria-hidden="true"></a>        <span class="bu">echo</span> <span class="st">&quot;✗ </span><span class="va">$PATH</span><span class="st"> - FAILED (</span><span class="va">$HTTP_CODE</span><span class="st">)&quot;</span></span>
<span id="cb15-25"><a href="#cb15-25" aria-hidden="true"></a>    <span class="kw">fi</span></span>
<span id="cb15-26"><a href="#cb15-26" aria-hidden="true"></a><span class="kw">done</span></span></code></pre>
</div>
<h3 id="next-steps">Next Steps</h3>
<p>You now have professional bulk update capabilities for WordPress sites at scale.</p>
<h4 id="recommended-learning-path">Recommended Learning Path</h4>
<p><strong>Week 1</strong>: Single site updates</p>
<ul>
<li>Practice safe update workflows</li>
<li>Implement backup strategies</li>
<li>Test rollback procedures</li>
</ul>
<p><strong>Week 2</strong>: Multi-site automation</p>
<ul>
<li>Build bulk update scripts</li>
<li>Add safety checks</li>
<li>Implement logging</li>
</ul>
<p><strong>Week 3</strong>: Staged rollouts</p>
<ul>
<li>Create test/production workflows</li>
<li>Set up monitoring</li>
<li>Automate notifications</li>
</ul>
<p><strong>Week 4</strong>: Full automation</p>
<ul>
<li>Schedule automated updates</li>
<li>Build health check systems</li>
<li>Document recovery procedures</li>
</ul>
<h4 id="advanced-topics">Advanced Topics</h4>
<ol type="1">
<li><strong><a href="#">Zero-Downtime Updates</a></strong> &#8211; Update without taking sites offline</li>
<li><strong><a href="#">Update Testing Automation</a></strong> &#8211; Automated functional testing</li>
<li><strong><a href="#">Large-Scale WordPress Management</a></strong> &#8211; Managing 500+ sites</li>
</ol>
<h4 id="get-more-resources">Get More Resources</h4>
<p><strong><a href="#">Download update automation scripts</a></strong> including:</p>
<ul>
<li>Complete bulk update system</li>
<li>Rollback automation</li>
<li>Health check templates</li>
</ul>
<p><strong><a href="/#get-started">Join our email course</a></strong> for:</p>
<ul>
<li>Weekly WP-CLI tutorials</li>
<li>WordPress automation strategies</li>
<li>Enterprise management techniques</li>
</ul>
<h3 id="conclusion">Conclusion</h3>
<p>Bulk updating WordPress sites with WP-CLI transforms a time-consuming, risky process into a fast, safe, automated workflow that scales to any number of installations.</p>
<p>What we covered:</p>
<p>✅ Single site update operations with safety checks ✅ Automated backup and rollback strategies ✅ Bulk update scripts for multiple sites ✅ Selective and staged update approaches ✅ Automated scheduling and monitoring ✅ Post-update testing and verification</p>
<p>Master these techniques, and you’ll manage WordPress updates across unlimited sites efficiently while maintaining security and minimizing downtime.</p>
<p><strong>Ready for more?</strong> Learn <a href="#">WordPress deployment automation</a> or <a href="#">continuous integration for WordPress</a>.</p>
<p><strong>Questions about bulk WordPress updates with WP-CLI?</strong> Drop a comment below!</p>
<p><strong>Found this helpful?</strong> Share with other WordPress developers and agencies.</p>
<p>The post <a href="https://wpclimastery.com/blog/bulk-update-wordpress-plugins-and-themes-across-multiple-sites-with-wp-cli/">Bulk Update WordPress Plugins and Themes Across Multiple Sites with WP-CLI</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Schedule WP-CLI Tasks with Cron: Complete Automation Guide</title>
		<link>https://wpclimastery.com/blog/how-to-schedule-wp-cli-tasks-with-cron-complete-automation-guide/</link>
					<comments>https://wpclimastery.com/blog/how-to-schedule-wp-cli-tasks-with-cron-complete-automation-guide/#respond</comments>
		
		<dc:creator><![CDATA[Krasen]]></dc:creator>
		<pubDate>Mon, 24 Nov 2025 11:16:24 +0000</pubDate>
				<category><![CDATA[WordPress Automation]]></category>
		<category><![CDATA[cron jobs wordpress]]></category>
		<category><![CDATA[linux cron wordpress]]></category>
		<category><![CDATA[schedule wordpress tasks]]></category>
		<category><![CDATA[wp-cli automation]]></category>
		<category><![CDATA[wp-cli cron]]></category>
		<guid isPermaLink="false">https://wpclimastery.com/?p=224</guid>

					<description><![CDATA[<p>Automating WordPress maintenance tasks saves time, reduces human error, and ensures critical operations run consistently. By combining WP-CLI with cron jobs, you can create a robust automation system that handles...</p>
<p>The post <a href="https://wpclimastery.com/blog/how-to-schedule-wp-cli-tasks-with-cron-complete-automation-guide/">How to Schedule WP-CLI Tasks with Cron: Complete Automation Guide</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Automating WordPress maintenance tasks saves time, reduces human error, and ensures critical operations run consistently. By combining WP-CLI with cron jobs, you can create a robust automation system that handles backups, updates, cleanup, and monitoring without manual intervention.</p>



<h2 class="wp-block-heading" id="understanding-cron-and-wp-cli-integration">Understanding Cron and WP-CLI Integration</h2>



<p>Cron is a time-based job scheduler in Unix-like operating systems that executes commands at specified intervals. When paired with WP-CLI, cron enables you to automate virtually any WordPress operation, from database optimization to content publishing.</p>



<p>The combination offers powerful capabilities: scheduled backups during off-peak hours, automatic plugin updates with rollback protection, regular security scans, database cleanup operations, and automated content workflows. This automation frees developers to focus on building features rather than routine maintenance.</p>



<h2 class="wp-block-heading" id="crontab-syntax-essentials">Crontab Syntax Essentials</h2>



<p>Understanding cron syntax is fundamental to creating effective scheduled tasks. A cron expression consists of five time fields followed by the command to execute:</p>



<pre class="wp-block-code"><code>* * * * * command-to-execute
│ │ │ │ │
│ │ │ │ └─── Day of week (0-7, where 0 and 7 are Sunday)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)
</code></pre>



<p>Common patterns include:</p>



<ul class="wp-block-list">
<li><code>0 2 * * *</code> &#8211; Daily at 2:00 AM</li>



<li><code>*/15 * * * *</code> &#8211; Every 15 minutes</li>



<li><code>0 0 * * 0</code> &#8211; Weekly on Sunday at midnight</li>



<li><code>0 3 1 * *</code> &#8211; Monthly on the 1st at 3:00 AM</li>



<li><code>0 */6 * * *</code> &#8211; Every 6 hours</li>
</ul>



<p>Special strings provide shortcuts:&nbsp;<code>@reboot</code>&nbsp;(on system startup),&nbsp;<code>@daily</code>&nbsp;(once per day),&nbsp;<code>@hourly</code>&nbsp;(once per hour),&nbsp;<code>@weekly</code>&nbsp;(once per week), and&nbsp;<code>@monthly</code>&nbsp;(once per month).</p>



<h2 class="wp-block-heading" id="setting-up-your-first-wp-cli-cron-job">Setting Up Your First WP-CLI Cron Job</h2>



<p>Begin by accessing your crontab editor with&nbsp;<code>crontab -e</code>. For a simple daily backup task:</p>



<pre class="wp-block-code"><code><em># Daily WordPress database backup at 2 AM</em>
0 2 * * * /usr/local/bin/wp --path=/var/www/html db export /backups/db-$(date +\%Y\%m\%d).sql --allow-root
</code></pre>



<p>Critical considerations when writing cron jobs: always use absolute paths for commands and files, set the correct user permissions, escape special characters (like&nbsp;<code>%</code>&nbsp;with backslash), and specify the WordPress path with the&nbsp;<code>--path</code>&nbsp;flag.</p>



<p>If WP-CLI runs as root, include the&nbsp;<code>--allow-root</code>&nbsp;flag. However, running as the web server user (like www-data) is generally safer:</p>



<pre class="wp-block-code"><code><em># Run as www-data user</em>
0 2 * * * sudo -u www-data /usr/local/bin/wp --path=/var/www/html db export /backups/db-$(date +\%Y\%m\%d).sql
</code></pre>



<h2 class="wp-block-heading" id="creating-a-maintenance-script">Creating a Maintenance Script</h2>



<p>Rather than cramming complex logic into crontab, create dedicated scripts that cron executes. This approach improves maintainability, enables version control, and facilitates testing.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># /opt/scripts/wp-maintenance.sh</em>
<em># WordPress daily maintenance automation</em>

set -euo pipefail

WP_PATH="/var/www/html"
BACKUP_DIR="/backups/wordpress"
LOG_FILE="/var/log/wp-maintenance.log"
DATE=$(date +%Y%m%d-%H%M%S)

<em># Logging function</em>
log() {
    echo "&#91;$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log "Starting WordPress maintenance"

<em># Create backup directory if it doesn't exist</em>
mkdir -p "$BACKUP_DIR"

<em># Database backup</em>
log "Creating database backup"
wp --path="$WP_PATH" db export "$BACKUP_DIR/database-$DATE.sql" || {
    log "ERROR: Database backup failed"
    exit 1
}

<em># Optimize database</em>
log "Optimizing database"
wp --path="$WP_PATH" db optimize

<em># Clean up old backups (keep last 7 days)</em>
log "Cleaning old backups"
find "$BACKUP_DIR" -name "database-*.sql" -mtime +7 -delete

<em># Update plugins (excluding specific ones)</em>
log "Updating plugins"
wp --path="$WP_PATH" plugin update --all --exclude=woocommerce,elementor

<em># Clear transients</em>
log "Clearing expired transients"
wp --path="$WP_PATH" transient delete --expired

<em># Generate thumbnail for recent uploads</em>
log "Regenerating recent thumbnails"
wp --path="$WP_PATH" media regenerate --yes --only-missing

<em># Clear object cache if available</em>
if wp --path="$WP_PATH" cache flush &amp;&gt;/dev/null; then
    log "Cache flushed successfully"
fi

log "Maintenance completed successfully"
</code></pre>



<p>Make the script executable and add to crontab:</p>



<pre class="wp-block-code"><code>chmod +x /opt/scripts/wp-maintenance.sh

<em># In crontab</em>
0 3 * * * /opt/scripts/wp-maintenance.sh
</code></pre>



<h2 class="wp-block-heading" id="advanced-scheduling-strategies">Advanced Scheduling Strategies</h2>



<p>Complex WordPress operations often require conditional logic, environment-specific configurations, and coordination between multiple tasks.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># Advanced scheduling with environment detection</em>

WP_PATH="/var/www/html"

<em># Detect environment</em>
ENVIRONMENT=$(wp --path="$WP_PATH" config get WP_ENVIRONMENT_TYPE)

case $ENVIRONMENT in
    production)
        <em># Conservative updates in production</em>
        wp --path="$WP_PATH" plugin update --minor
        wp --path="$WP_PATH" core update --minor
        ;;
    staging)
        <em># More aggressive updates in staging</em>
        wp --path="$WP_PATH" plugin update --all
        wp --path="$WP_PATH" core update
        wp --path="$WP_PATH" theme update --all
        ;;
    development)
        <em># Full updates including development versions</em>
        wp --path="$WP_PATH" plugin update --all
        wp --path="$WP_PATH" core update --version=nightly
        ;;
esac
</code></pre>



<p>For coordinated tasks across multiple sites:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># Multi-site update coordination</em>

SITES=(
    "/var/www/site1"
    "/var/www/site2"
    "/var/www/site3"
)

for site in "${SITES&#91;@]}"; do
    echo "Processing: $site"

    <em># Check if site is healthy before updating</em>
    if wp --path="$site" core is-installed; then
        wp --path="$site" core update --minor
        wp --path="$site" plugin update --all --exclude=custom-plugin

        <em># Verify site is still functional</em>
        if ! wp --path="$site" core verify-checksums; then
            echo "WARNING: Checksum verification failed for $site"
            <em># Send alert notification</em>
        fi
    else
        echo "ERROR: WordPress not properly installed at $site"
    fi
done
</code></pre>



<h2 class="wp-block-heading" id="error-handling-and-notifications">Error Handling and Notifications</h2>



<p>Production cron jobs require robust error handling and notification systems to alert administrators when problems occur.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># wp-backup-with-notifications.sh</em>

set -euo pipefail

ADMIN_EMAIL="admin@example.com"
WP_PATH="/var/www/html"
BACKUP_DIR="/backups"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)

send_alert() {
    local subject="$1"
    local message="$2"
    echo "$message" | mail -s "$subject" "$ADMIN_EMAIL"
}

<em># Trap errors</em>
trap 'send_alert "WordPress Backup Failed" "Backup script failed at line $LINENO"' ERR

<em># Check disk space</em>
AVAILABLE_SPACE=$(df "$BACKUP_DIR" | tail -1 | awk '{print $4}')
if &#91; "$AVAILABLE_SPACE" -lt 1048576 ]; then  <em># Less than 1GB</em>
    send_alert "Low Disk Space Warning" "Available space: ${AVAILABLE_SPACE}KB"
    exit 1
fi

<em># Perform backup</em>
wp --path="$WP_PATH" db export "$BACKUP_DIR/db-$TIMESTAMP.sql"

<em># Backup files</em>
tar -czf "$BACKUP_DIR/files-$TIMESTAMP.tar.gz" -C "$WP_PATH" wp-content/uploads

<em># Verify backup integrity</em>
if &#91; -f "$BACKUP_DIR/db-$TIMESTAMP.sql" ] &amp;&amp; &#91; -f "$BACKUP_DIR/files-$TIMESTAMP.tar.gz" ]; then
    BACKUP_SIZE=$(du -sh "$BACKUP_DIR" | cut -f1)
    send_alert "Backup Completed Successfully" "Backup size: $BACKUP_SIZE"
else
    send_alert "Backup Verification Failed" "One or more backup files are missing"
    exit 1
fi
</code></pre>



<h2 class="wp-block-heading" id="logging-and-monitoring">Logging and Monitoring</h2>



<p>Comprehensive logging helps troubleshoot issues and track automation performance over time.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># Enhanced logging example</em>

LOG_DIR="/var/log/wp-automation"
mkdir -p "$LOG_DIR"

<em># Create dated log file</em>
LOG_FILE="$LOG_DIR/maintenance-$(date +%Y%m%d).log"

<em># Logging function with levels</em>
log() {
    local level=$1
    shift
    local message="$@"
    echo "&#91;$(date '+%Y-%m-%d %H:%M:%S')] &#91;$level] $message" &gt;&gt; "$LOG_FILE"
}

<em># Execute WP-CLI command with logging</em>
wp_execute() {
    local cmd="$@"
    log "INFO" "Executing: wp $cmd"

    if output=$(wp $cmd 2&gt;&amp;1); then
        log "SUCCESS" "Command completed: $output"
        return 0
    else
        log "ERROR" "Command failed: $output"
        return 1
    fi
}

<em># Usage</em>
wp_execute --path=/var/www/html plugin list --status=active
wp_execute --path=/var/www/html core update --minor
</code></pre>



<p>Monitor log files with automated analysis:</p>



<pre class="wp-block-code"><code><em># Cron job to analyze logs and send daily summary</em>
0 8 * * * /opt/scripts/analyze-logs.sh

<em># analyze-logs.sh</em>
<em>#!/bin/bash</em>
LOG_DIR="/var/log/wp-automation"
YESTERDAY=$(date -d "yesterday" +%Y%m%d)
LOG_FILE="$LOG_DIR/maintenance-$YESTERDAY.log"

if &#91; -f "$LOG_FILE" ]; then
    ERRORS=$(grep -c "\&#91;ERROR\]" "$LOG_FILE" || true)
    SUCCESSES=$(grep -c "\&#91;SUCCESS\]" "$LOG_FILE" || true)

    mail -s "WP Automation Summary $YESTERDAY" admin@example.com &lt;&lt;EOF
Daily WordPress Automation Summary

Successful operations: $SUCCESSES
Failed operations: $ERRORS

Recent errors:
$(grep "\&#91;ERROR\]" "$LOG_FILE" | tail -5)
EOF
fi
</code></pre>



<h2 class="wp-block-heading" id="security-considerations">Security Considerations</h2>



<p>Secure your cron jobs by limiting file permissions, using dedicated service accounts, and protecting sensitive information.</p>



<pre class="wp-block-code"><code><em># Create dedicated user for WordPress automation</em>
sudo useradd -r -s /bin/bash wpautomation

<em># Set appropriate permissions</em>
chmod 700 /opt/scripts/wp-maintenance.sh
chown wpautomation:wpautomation /opt/scripts/wp-maintenance.sh

<em># Store credentials securely</em>
cat &gt; /home/wpautomation/.wp-cli-credentials &lt;&lt; EOF
SMTP_PASSWORD=secure_password
API_KEY=secret_key
EOF

chmod 600 /home/wpautomation/.wp-cli-credentials
chown wpautomation:wpautomation /home/wpautomation/.wp-cli-credentials

<em># Crontab for wpautomation user</em>
sudo -u wpautomation crontab -e
</code></pre>



<h2 class="wp-block-heading" id="testing-cron-jobs">Testing Cron Jobs</h2>



<p>Before deploying to production, test your cron jobs thoroughly:</p>



<pre class="wp-block-code"><code><em># Run manually to verify behavior</em>
/opt/scripts/wp-maintenance.sh

<em># Test with verbose WP-CLI output</em>
wp --path=/var/www/html plugin update --all --dry-run --debug

<em># Verify cron syntax</em>
<em># Install cron expression parser</em>
npm install -g cron-validate

<em># Check if your cron expression is valid</em>
echo "0 3 * * *" | cron-validate
</code></pre>



<p>Use temporary increased frequency for testing:</p>



<pre class="wp-block-code"><code><em># Test frequency (every 5 minutes)</em>
*/5 * * * * /opt/scripts/test-automation.sh

<em># Production frequency (daily at 3 AM)</em>
<em># 0 3 * * * /opt/scripts/wp-maintenance.sh</em>
</code></pre>



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



<p>Always redirect output appropriately &#8211; either to log files or use&nbsp;<code>&gt;/dev/null 2&gt;&amp;1</code>&nbsp;to suppress. Include timeout mechanisms for long-running operations to prevent hanging processes. Implement file locking to prevent concurrent execution when scripts shouldn&#8217;t overlap.</p>



<p>Use environment variables for configuration rather than hardcoding values. Document each cron job with comments explaining its purpose and frequency. Regularly review and audit scheduled tasks to remove obsolete jobs.</p>



<p>Consider using cron management tools like&nbsp;<code>whenever</code>&nbsp;(Ruby) or&nbsp;<code>cronitor</code>&nbsp;for monitoring. Set up health checks to verify cron jobs are executing as expected.</p>



<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 Commands Reference</a></li>



<li><a href="https://crontab.guru/">Crontab Guru &#8211; Cron Schedule Expression Editor</a></li>



<li><a href="https://www.man7.org/linux/man-pages/man5/crontab.5.html">Linux Cron Tutorial</a></li>



<li><a href="https://wordpress.org/support/article/wordpress-backups/">WordPress Maintenance Best Practices</a></li>



<li><a href="https://developer.wordpress.org/advanced-administration/">Automated WordPress Deployment Guide</a></li>
</ul>
<p>The post <a href="https://wpclimastery.com/blog/how-to-schedule-wp-cli-tasks-with-cron-complete-automation-guide/">How to Schedule WP-CLI Tasks with Cron: Complete Automation Guide</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wpclimastery.com/blog/how-to-schedule-wp-cli-tasks-with-cron-complete-automation-guide/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Automate WordPress Backups with WP-CLI and Bash (Step-by-Step)</title>
		<link>https://wpclimastery.com/blog/automate-wordpress-backups-with-wp-cli-and-bash-step-by-step/</link>
					<comments>https://wpclimastery.com/blog/automate-wordpress-backups-with-wp-cli-and-bash-step-by-step/#respond</comments>
		
		<dc:creator><![CDATA[Krasen]]></dc:creator>
		<pubDate>Thu, 20 Nov 2025 09:00:00 +0000</pubDate>
				<category><![CDATA[WordPress Automation]]></category>
		<category><![CDATA[automated wordpress backups]]></category>
		<category><![CDATA[bash backup script]]></category>
		<category><![CDATA[wordpress backup automation]]></category>
		<category><![CDATA[wordpress devops]]></category>
		<category><![CDATA[wp-cli backup]]></category>
		<guid isPermaLink="false">https://wpclimastery.com/?p=12</guid>

					<description><![CDATA[<p>Losing a WordPress site to a server crash, hack, or accidental deletion is a nightmare no developer should experience. Yet according to&#160;WordPress security statistics, over 30,000 websites are hacked every...</p>
<p>The post <a href="https://wpclimastery.com/blog/automate-wordpress-backups-with-wp-cli-and-bash-step-by-step/">Automate WordPress Backups with WP-CLI and Bash (Step-by-Step)</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Losing a WordPress site to a server crash, hack, or accidental deletion is a nightmare no developer should experience. Yet according to&nbsp;<a href="https://www.wpwhitesecurity.com/statistics-70-percent-wordpress-installations-vulnerable-attacks/">WordPress security statistics</a>, over 30,000 websites are hacked every day, and many have no recent backups.</p>



<p>Manual backups are tedious, error-prone, and easy to forget. The solution? Automated backups using WP-CLI and Bash that run like clockwork—whether you&#8217;re awake or asleep.</p>



<p>In this comprehensive guide, you&#8217;ll learn how to build a production-ready WordPress backup automation system from scratch. By the end, you&#8217;ll have a script that automatically backs up your database, files, compresses everything, manages retention policies, and even notifies you of success or failure.</p>



<h3 class="wp-block-heading" id="why-automate-wordpress-backups-why-automate">Why Automate WordPress Backups?</h3>



<h4 class="wp-block-heading" id="the-cost-of-no-backups">The Cost of No Backups</h4>



<p><strong>Real scenario</strong>: A client&#8217;s WordPress site gets hacked at 3 AM. The attacker deletes the database and replaces files with malware. Without automated backups, you&#8217;re looking at:</p>



<ul class="wp-block-list">
<li><strong>Hours of downtime</strong>&nbsp;(lost revenue)</li>



<li><strong>Lost content</strong>&nbsp;(months of blog posts, products, customer data)</li>



<li><strong>Damaged reputation</strong>&nbsp;(customers lose trust)</li>



<li><strong>Potential data breach liability</strong></li>
</ul>



<p>With automated backups, you restore from last night&#8217;s backup and are back online in 15 minutes.</p>



<h4 class="wp-block-heading" id="manual-backups-dont-scale">Manual Backups Don&#8217;t Scale</h4>



<p><strong>The manual backup problem:</strong></p>



<ul class="wp-block-list">
<li>Requires you to remember (you won&#8217;t always)</li>



<li>Takes 10-15 minutes per site</li>



<li>Easy to make mistakes (wrong directory, incomplete backup)</li>



<li>Doesn&#8217;t work at 3 AM when you&#8217;re sleeping</li>
</ul>



<p><strong>Automated backups solve this:</strong></p>



<ul class="wp-block-list">
<li>Run on schedule without human intervention</li>



<li>Always complete (or notify you of failures)</li>



<li>Work 24/7/365</li>



<li>Scale to hundreds of sites with the same script</li>
</ul>



<h4 class="wp-block-heading" id="statistics-that-matter">Statistics That Matter</h4>



<p>According to&nbsp;<a href="https://www.codeinwp.com/blog/wordpress-statistics/">CodeinWP&#8217;s WordPress statistics</a>:</p>



<ul class="wp-block-list">
<li><strong>30%</strong>&nbsp;of websites have no backup at all</li>



<li><strong>60%</strong>&nbsp;of small businesses never recover from data loss</li>



<li>The average cost of downtime is&nbsp;<strong>$5,600 per minute</strong>&nbsp;for e-commerce sites</li>
</ul>



<p>Can you afford NOT to automate backups?</p>



<h3 class="wp-block-heading" id="what-to-backup-in-wordpress-what-to-backup">What to Backup in WordPress</h3>



<p>A complete WordPress backup includes two critical components:</p>



<h4 class="wp-block-heading" id="1-database-mysqlmariadb">1. Database (MySQL/MariaDB)</h4>



<p>The database contains:</p>



<ul class="wp-block-list">
<li>All posts, pages, and custom post types</li>



<li>Comments and user data</li>



<li>Site settings and options</li>



<li>Plugin and theme configurations</li>



<li>Widgets and menus</li>
</ul>



<p><strong>Size</strong>: Usually 5-50 MB for small sites, up to 500 MB+ for large sites</p>



<p><strong>Backup frequency</strong>: Daily minimum, hourly for high-traffic sites</p>



<h4 class="wp-block-heading" id="2-file-system">2. File System</h4>



<p>Essential files include:</p>



<ul class="wp-block-list">
<li><strong>wp-content/uploads/</strong>&nbsp;&#8211; All media files (images, videos, PDFs)</li>



<li><strong>wp-content/themes/</strong>&nbsp;&#8211; Custom themes</li>



<li><strong>wp-content/plugins/</strong>&nbsp;&#8211; Installed plugins</li>



<li><strong>wp-config.php</strong>&nbsp;&#8211; Database configuration (contains credentials)</li>



<li><strong>.htaccess</strong>&nbsp;&#8211; Server configuration</li>
</ul>



<p><strong>Size</strong>: Can range from 100 MB to 50+ GB depending on media library</p>



<p><strong>Backup frequency</strong>: Daily for active sites, weekly for static sites</p>



<h4 class="wp-block-heading" id="what-you-can-skip">What You Can Skip</h4>



<p>To save space and time, you can exclude:</p>



<ul class="wp-block-list">
<li><strong>wp-content/cache/</strong>&nbsp;&#8211; Temporary cache files</li>



<li><strong>wp-content/backups/</strong>&nbsp;&#8211; Avoid backing up backups</li>



<li><strong>wp-core files</strong>&nbsp;&#8211; Can be re-downloaded from WordPress.org</li>
</ul>



<p>For very large media libraries (10+ GB), consider backing up uploads separately to cloud storage.</p>



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



<p>Before building the automation script, ensure you have:</p>



<h4 class="wp-block-heading" id="required-software">Required Software</h4>



<ol class="wp-block-list">
<li><strong>WP-CLI installed</strong>&nbsp;&#8211;&nbsp;<a href="https://file+.vscode-resource.vscode-cdn.net/blog/install-configure-wpcli-ubuntu-debian">Installation guide</a></li>



<li><strong>Bash shell</strong>&nbsp;&#8211; Standard on Linux/Unix/macOS</li>



<li><strong>SSH access</strong>&nbsp;to your server</li>



<li><strong>Basic Bash knowledge</strong>&nbsp;&#8211;&nbsp;<a href="https://file+.vscode-resource.vscode-cdn.net/blog/bash-functions-wordpress-automation">Bash functions guide</a></li>
</ol>



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



<ul class="wp-block-list">
<li><strong>Disk space</strong>: At least 2x your WordPress installation size for backups</li>



<li><strong>Write permissions</strong>: Access to create backup directories</li>



<li><strong>WP-CLI access</strong>: Ability to run&nbsp;<code>wp</code>&nbsp;commands</li>



<li><strong>Cron access</strong>: For scheduling (optional but recommended)</li>
</ul>



<h4 class="wp-block-heading" id="test-your-setup">Test Your Setup</h4>



<p>Verify WP-CLI works:</p>



<pre class="wp-block-code"><code>cd /var/www/html
wp core version
</code></pre>



<p>If this returns your WordPress version, you&#8217;re ready to proceed.</p>



<h3 class="wp-block-heading" id="building-the-backup-script-step-by-step-building-script">Building the Backup Script Step-by-Step</h3>



<p>Let&#8217;s build the backup script incrementally, starting simple and adding features.</p>



<h4 class="wp-block-heading" id="step-1-basic-structure">Step 1: Basic Structure</h4>



<p>Create a new file:</p>



<pre class="wp-block-code"><code>nano ~/wp-backup.sh
</code></pre>



<p>Add the basic structure:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em>#</em>
<em># WordPress Backup Automation Script</em>
<em># Backs up database and files for a WordPress installation</em>
<em>#</em>

set -euo pipefail  <em># Exit on error, undefined vars, pipe failures</em>

<em># Configuration</em>
WP_PATH="/var/www/html"
BACKUP_DIR="/var/backups/wordpress"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")

echo "Starting WordPress backup..."
echo "Timestamp: ${TIMESTAMP}"

<em># Script logic will go here</em>

echo "Backup completed!"
</code></pre>



<p>Make it executable:</p>



<pre class="wp-block-code"><code>chmod +x ~/wp-backup.sh
</code></pre>



<p><strong>What this does:</strong></p>



<ul class="wp-block-list">
<li><code>set -euo pipefail</code>&nbsp;ensures the script exits on errors</li>



<li>Variables store paths and timestamp</li>



<li>Basic output shows script is running</li>
</ul>



<h3 class="wp-block-heading" id="database-backup-with-wp-cli-database-backup">Database Backup with WP-CLI</h3>



<h4 class="wp-block-heading" id="export-database-to-sql-file">Export Database to SQL File</h4>



<p>Add database backup function:</p>



<pre class="wp-block-code"><code>backup_database() {
    local wp_path=$1
    local backup_dir=$2
    local timestamp=$3

    local db_file="${backup_dir}/database_${timestamp}.sql"

    echo "Backing up database..."

    if wp db export "$db_file" --path="$wp_path" --quiet; then
        echo "✓ Database exported: ${db_file}"
        echo "$db_file"
        return 0
    else
        echo "✗ Database export failed" &gt;&amp;2
        return 1
    fi
}
</code></pre>



<p><strong>How this works:</strong></p>



<ul class="wp-block-list">
<li><code>wp db export</code>&nbsp;dumps the entire database to a .sql file</li>



<li><code>--quiet</code>&nbsp;suppresses verbose output</li>



<li>Returns the filename on success, empty on failure</li>
</ul>



<h4 class="wp-block-heading" id="add-compression">Add Compression</h4>



<p>Compress the SQL file to save space:</p>



<pre class="wp-block-code"><code>backup_database() {
    local wp_path=$1
    local backup_dir=$2
    local timestamp=$3

    local db_file="${backup_dir}/database_${timestamp}.sql"

    echo "Backing up database..."

    if wp db export "$db_file" --path="$wp_path" --quiet; then
        echo "✓ Database exported: ${db_file}"

        <em># Compress with gzip</em>
        gzip "$db_file"
        echo "✓ Database compressed: ${db_file}.gz"

        echo "${db_file}.gz"
        return 0
    else
        echo "✗ Database export failed" &gt;&amp;2
        return 1
    fi
}
</code></pre>



<p><strong>Compression benefits:</strong></p>



<ul class="wp-block-list">
<li>Typical compression ratio: 10:1 (50 MB → 5 MB)</li>



<li>Saves disk space and transfer bandwidth</li>



<li>Standard format compatible with all restore tools</li>
</ul>



<h4 class="wp-block-heading" id="database-backup-best-practices">Database Backup Best Practices</h4>



<p><strong>Security considerations:</strong></p>



<ul class="wp-block-list">
<li>SQL files contain database credentials in plain text</li>



<li>Ensure backup directory has restricted permissions (700)</li>



<li>Never store backups in web-accessible directories</li>
</ul>



<pre class="wp-block-code"><code><em># Secure backup directory</em>
mkdir -p "$BACKUP_DIR"
chmod 700 "$BACKUP_DIR"
</code></pre>



<p><strong>Performance tips:</strong></p>



<ul class="wp-block-list">
<li>Large databases (500+ MB) can take 1-5 minutes to export</li>



<li>Run backups during low-traffic periods (2-4 AM)</li>



<li>Consider incremental backups for massive databases</li>
</ul>



<h3 class="wp-block-heading" id="file-system-backup-file-backup">File System Backup</h3>



<h4 class="wp-block-heading" id="backup-wordpress-files-with-tar">Backup WordPress Files with tar</h4>



<p>Add file backup function:</p>



<pre class="wp-block-code"><code>backup_files() {
    local wp_path=$1
    local backup_dir=$2
    local timestamp=$3

    local files_archive="${backup_dir}/files_${timestamp}.tar.gz"

    echo "Backing up files..."

    <em># Exclude cache and temporary files</em>
    local exclude_opts="--exclude=wp-content/cache --exclude=wp-content/backup*"

    if tar -czf "$files_archive" \
        $exclude_opts \
        -C "$(dirname $wp_path)" \
        "$(basename $wp_path)" 2&gt;/dev/null; then

        echo "✓ Files archived: ${files_archive}"
        echo "$files_archive"
        return 0
    else
        echo "✗ File backup failed" &gt;&amp;2
        return 1
    fi
}
</code></pre>



<p><strong>What this does:</strong></p>



<ul class="wp-block-list">
<li><code>tar -czf</code>&nbsp;creates a compressed archive</li>



<li><code>-C</code>&nbsp;changes directory before archiving</li>



<li><code>--exclude</code>&nbsp;skips cache and backup directories</li>



<li>Output is redirected to suppress tar warnings</li>
</ul>



<h4 class="wp-block-heading" id="conditional-uploads-backup">Conditional Uploads Backup</h4>



<p>For sites with large media libraries, make uploads optional:</p>



<pre class="wp-block-code"><code>backup_files() {
    local wp_path=$1
    local backup_dir=$2
    local timestamp=$3
    local include_uploads=${4:-true}  <em># Default to true</em>

    local files_archive="${backup_dir}/files_${timestamp}.tar.gz"

    echo "Backing up files..."

    <em># Build exclude options</em>
    local exclude_opts="--exclude=wp-content/cache --exclude=wp-content/backup*"

    if &#91; "$include_uploads" = "false" ]; then
        echo "  (Excluding uploads directory)"
        exclude_opts="${exclude_opts} --exclude=wp-content/uploads"
    fi

    if tar -czf "$files_archive" \
        $exclude_opts \
        -C "$(dirname $wp_path)" \
        "$(basename $wp_path)" 2&gt;/dev/null; then

        echo "✓ Files archived: ${files_archive}"
        echo "$files_archive"
        return 0
    else
        echo "✗ File backup failed" &gt;&amp;2
        return 1
    fi
}

<em># Usage</em>
backup_files /var/www/html /backups $TIMESTAMP false  <em># Skip uploads</em>
</code></pre>



<p><strong>When to exclude uploads:</strong></p>



<ul class="wp-block-list">
<li>Media library &gt; 10 GB</li>



<li>Uploads stored on CDN or cloud storage</li>



<li>Backup uploads separately to S3/Backblaze</li>
</ul>



<h3 class="wp-block-heading" id="compression-and-storage-optimization-compression">Compression and Storage Optimization</h3>



<h4 class="wp-block-heading" id="compression-comparison">Compression Comparison</h4>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Method</th><th>Ratio</th><th>Speed</th><th>CPU Usage</th></tr></thead><tbody><tr><td>gzip (default)</td><td>10:1</td><td>Fast</td><td>Low</td></tr><tr><td>bzip2</td><td>15:1</td><td>Slow</td><td>Medium</td></tr><tr><td>xz</td><td>20:1</td><td>Very Slow</td><td>High</td></tr></tbody></table></figure>



<p><strong>Recommendation</strong>: Use gzip for daily backups (best speed/size balance)</p>



<h4 class="wp-block-heading" id="combined-archive">Combined Archive</h4>



<p>Create a single archive containing both database and files:</p>



<pre class="wp-block-code"><code>create_combined_backup() {
    local db_backup=$1
    local files_backup=$2
    local backup_dir=$3
    local timestamp=$4

    local combined="${backup_dir}/wordpress_backup_${timestamp}.tar.gz"
    local temp_dir=$(mktemp -d)

    <em># Copy both backups to temp directory</em>
    cp "$db_backup" "$files_backup" "$temp_dir/"

    <em># Create combined archive</em>
    tar -czf "$combined" -C "$temp_dir" .

    <em># Clean up</em>
    rm -rf "$temp_dir"
    rm -f "$db_backup" "$files_backup"

    echo "✓ Combined backup created: ${combined}"
    echo "$combined"
}
</code></pre>



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



<ul class="wp-block-list">
<li>Single file to manage</li>



<li>Easier to transfer and restore</li>



<li>Atomic backup (all or nothing)</li>
</ul>



<h3 class="wp-block-heading" id="retention-policies-retention">Retention Policies</h3>



<h4 class="wp-block-heading" id="automatic-cleanup-of-old-backups">Automatic Cleanup of Old Backups</h4>



<p>Prevent backups from filling your disk:</p>



<pre class="wp-block-code"><code>cleanup_old_backups() {
    local backup_dir=$1
    local retention_days=${2:-7}  <em># Default 7 days</em>

    echo "Cleaning up backups older than ${retention_days} days..."

    local deleted=$(find "$backup_dir" \
        -name "wordpress_backup_*.tar.gz" \
        -type f \
        -mtime +${retention_days} \
        -delete -print | wc -l)

    if &#91; "$deleted" -gt 0 ]; then
        echo "✓ Deleted ${deleted} old backup(s)"
    else
        echo "  No old backups to clean up"
    fi
}

<em># Usage</em>
cleanup_old_backups /var/backups/wordpress 7  <em># Keep 7 days</em>
</code></pre>



<h4 class="wp-block-heading" id="retention-strategy-recommendations">Retention Strategy Recommendations</h4>



<p><strong>Daily backups:</strong></p>



<ul class="wp-block-list">
<li>Keep: Last 7 days</li>



<li>Disk usage: ~7x backup size</li>
</ul>



<p><strong>Weekly backups:</strong></p>



<ul class="wp-block-list">
<li>Keep: Last 4 weeks (28 days)</li>



<li>Disk usage: ~4x backup size</li>
</ul>



<p><strong>Monthly backups:</strong></p>



<ul class="wp-block-list">
<li>Keep: Last 12 months (365 days)</li>



<li>Disk usage: ~12x backup size</li>
</ul>



<p><strong>3-2-1 Rule:</strong></p>



<ul class="wp-block-list">
<li><strong>3</strong>&nbsp;copies of data (original + 2 backups)</li>



<li><strong>2</strong>&nbsp;different storage types (local + cloud)</li>



<li><strong>1</strong>&nbsp;copy off-site</li>
</ul>



<h3 class="wp-block-heading" id="email-notifications-notifications">Email Notifications</h3>



<h4 class="wp-block-heading" id="send-email-on-successfailure">Send Email on Success/Failure</h4>



<pre class="wp-block-code"><code>send_notification() {
    local subject=$1
    local message=$2
    local email="${NOTIFICATION_EMAIL:-}"

    if &#91; -z "$email" ]; then
        return 0  <em># No email configured</em>
    fi

    if command -v mail &amp;&gt; /dev/null; then
        echo -e "$message" | mail -s "$subject" "$email"
        echo "✓ Email sent to ${email}"
    else
        echo "⚠ mail command not found (install mailutils)"
    fi
}

<em># Usage on success</em>
send_notification \
    "WordPress Backup SUCCESS" \
    "Backup completed successfully\nFile: ${backup_file}\nSize: ${size}"

<em># Usage on failure</em>
send_notification \
    "WordPress Backup FAILED" \
    "Backup failed during database export\nCheck logs for details"
</code></pre>



<h4 class="wp-block-heading" id="install-mail-utilities">Install Mail Utilities</h4>



<pre class="wp-block-code"><code><em># Ubuntu/Debian</em>
sudo apt-get install mailutils

<em># CentOS/RHEL</em>
sudo yum install mailx
</code></pre>



<p><strong>Configure email in script:</strong></p>



<pre class="wp-block-code"><code><em># At top of script</em>
NOTIFICATION_EMAIL="admin@yourdomain.com"
</code></pre>



<h3 class="wp-block-heading" id="scheduling-with-cron-cron-scheduling">Scheduling with Cron</h3>



<h4 class="wp-block-heading" id="add-to-crontab">Add to Crontab</h4>



<p>Schedule daily backups at 2 AM:</p>



<pre class="wp-block-code"><code><em># Edit crontab</em>
crontab -e

<em># Add this line</em>
0 2 * * * /home/username/wp-backup.sh &gt;&gt; /var/log/wp-backup.log 2&gt;&amp;1
</code></pre>



<p><strong>Cron schedule breakdown:</strong></p>



<ul class="wp-block-list">
<li><code>0</code>&nbsp;&#8211; Minute (0)</li>



<li><code>2</code>&nbsp;&#8211; Hour (2 AM)</li>



<li><code>*</code>&nbsp;&#8211; Day of month (every day)</li>



<li><code>*</code>&nbsp;&#8211; Month (every month)</li>



<li><code>*</code>&nbsp;&#8211; Day of week (every day)</li>
</ul>



<h4 class="wp-block-heading" id="common-cron-schedules">Common Cron Schedules</h4>



<pre class="wp-block-code"><code><em># Every 6 hours</em>
0 */6 * * * /home/username/wp-backup.sh

<em># Daily at 3:30 AM</em>
30 3 * * * /home/username/wp-backup.sh

<em># Weekly on Sundays at 2 AM</em>
0 2 * * 0 /home/username/wp-backup.sh

<em># Twice daily (6 AM and 6 PM)</em>
0 6,18 * * * /home/username/wp-backup.sh
</code></pre>



<p>Use&nbsp;<a href="https://crontab.guru/">crontab.guru</a>&nbsp;to generate cron schedules visually.</p>



<h4 class="wp-block-heading" id="verify-cron-is-running">Verify Cron is Running</h4>



<pre class="wp-block-code"><code><em># List your cron jobs</em>
crontab -l

<em># Check cron logs (Ubuntu/Debian)</em>
grep CRON /var/log/syslog

<em># Check your backup logs</em>
tail -f /var/log/wp-backup.log
</code></pre>



<h3 class="wp-block-heading" id="testing-your-backups-testing">Testing Your Backups</h3>



<p><strong>Critical truth</strong>: A backup you haven&#8217;t tested is worthless.</p>



<h4 class="wp-block-heading" id="test-restore-procedure">Test Restore Procedure</h4>



<ol class="wp-block-list">
<li><strong>Extract backup:</strong></li>
</ol>



<pre class="wp-block-code"><code>cd /var/backups/wordpress
tar -xzf wordpress_backup_20250220_020000.tar.gz
</code></pre>



<ol start="2" class="wp-block-list">
<li><strong>Restore database:</strong></li>
</ol>



<pre class="wp-block-code"><code>gunzip database_20250220_020000.sql.gz
wp db import database_20250220_020000.sql --path=/var/www/html
</code></pre>



<ol start="3" class="wp-block-list">
<li><strong>Restore files:</strong></li>
</ol>



<pre class="wp-block-code"><code>tar -xzf files_20250220_020000.tar.gz -C /var/www/
</code></pre>



<ol start="4" class="wp-block-list">
<li><strong>Verify site:</strong>&nbsp;Visit your site and check that content is intact.</li>
</ol>



<h4 class="wp-block-heading" id="test-on-staging-environment">Test on Staging Environment</h4>



<p><strong>Best practice</strong>: Never test restores on production.</p>



<p>Create a staging environment:</p>



<pre class="wp-block-code"><code><em># Copy WordPress to staging</em>
cp -r /var/www/html /var/www/staging

<em># Update wp-config.php with staging database</em>
wp config set DB_NAME staging_db --path=/var/www/staging

<em># Test restore there</em>
wp db import backup.sql --path=/var/www/staging
</code></pre>



<h4 class="wp-block-heading" id="backup-verification-script">Backup Verification Script</h4>



<pre class="wp-block-code"><code>verify_backup() {
    local backup_file=$1

    echo "Verifying backup integrity..."

    <em># Check file exists</em>
    if &#91; ! -f "$backup_file" ]; then
        echo "✗ Backup file not found"
        return 1
    fi

    <em># Check file size (should be &gt; 1KB)</em>
    local size=$(stat -f%z "$backup_file" 2&gt;/dev/null || stat -c%s "$backup_file")
    if &#91; "$size" -lt 1024 ]; then
        echo "✗ Backup file suspiciously small (${size} bytes)"
        return 1
    fi

    <em># Test tar integrity</em>
    if tar -tzf "$backup_file" &amp;&gt;/dev/null; then
        echo "✓ Backup archive is valid"
        return 0
    else
        echo "✗ Backup archive is corrupted"
        return 1
    fi
}
</code></pre>



<h3 class="wp-block-heading" id="off-site-backup-storage-offsite-storage">Off-Site Backup Storage</h3>



<p>Local backups protect against accidental deletion. Off-site backups protect against server failure, fires, and catastrophic events.</p>



<h4 class="wp-block-heading" id="option-1-aws-s3">Option 1: AWS S3</h4>



<pre class="wp-block-code"><code><em># Install AWS CLI</em>
sudo apt-get install awscli

<em># Configure credentials</em>
aws configure

<em># Sync backups to S3</em>
aws s3 sync /var/backups/wordpress/ s3://your-bucket/wordpress-backups/
</code></pre>



<p><strong>Add to backup script:</strong></p>



<pre class="wp-block-code"><code><em># After backup completes</em>
echo "Syncing to AWS S3..."
if aws s3 cp "$backup_file" s3://your-bucket/backups/; then
    echo "✓ Uploaded to S3"
else
    echo "✗ S3 upload failed" &gt;&amp;2
fi
</code></pre>



<p><strong>S3 pricing</strong>: ~$0.023/GB/month (very affordable)</p>



<h4 class="wp-block-heading" id="option-2-rsync-to-remote-server">Option 2: Rsync to Remote Server</h4>



<pre class="wp-block-code"><code><em># Sync to remote server via SSH</em>
rsync -avz /var/backups/wordpress/ \
    user@backup-server.com:/backups/wordpress/
</code></pre>



<p><strong>Add to backup script:</strong></p>



<pre class="wp-block-code"><code><em># After backup completes</em>
echo "Syncing to remote server..."
if rsync -avz "$backup_file" user@backup-server.com:/backups/; then
    echo "✓ Synced to remote server"
else
    echo "✗ Remote sync failed" &gt;&amp;2
fi
</code></pre>



<h4 class="wp-block-heading" id="option-3-backblaze-b2-budget-friendly">Option 3: Backblaze B2 (Budget-Friendly)</h4>



<p>Backblaze B2 costs&nbsp;<strong>1/4 of S3</strong>&nbsp;($0.005/GB/month):</p>



<pre class="wp-block-code"><code><em># Install B2 CLI</em>
pip install b2

<em># Configure</em>
b2 authorize-account &lt;key_id&gt; &lt;app_key&gt;

<em># Upload</em>
b2 upload-file &lt;bucket_name&gt; "$backup_file" backups/$(basename $backup_file)
</code></pre>



<p>Learn more:&nbsp;<a href="https://www.backblaze.com/b2/docs/">Backblaze B2 Documentation</a></p>



<h3 class="wp-block-heading" id="complete-production-script-complete-script">Complete Production Script</h3>



<p>Here&#8217;s the full production-ready script with all features:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em>#</em>
<em># WordPress Backup Automation Script</em>
<em># Complete production-ready version</em>
<em>#</em>

set -euo pipefail

<em>#############################################</em>
<em># CONFIGURATION</em>
<em>#############################################</em>

WP_PATH="/var/www/html"
BACKUP_DIR="/var/backups/wordpress"
RETENTION_DAYS=7
NOTIFICATION_EMAIL="admin@yourdomain.com"
INCLUDE_UPLOADS=true

<em>#############################################</em>
<em># COLORS</em>
<em>#############################################</em>

RED='\033&#91;0;31m'
GREEN='\033&#91;0;32m'
YELLOW='\033&#91;1;33m'
NC='\033&#91;0m'

<em>#############################################</em>
<em># FUNCTIONS</em>
<em>#############################################</em>

log_info() {
    echo -e "${GREEN}&#91;INFO]${NC} $*"
}

log_error() {
    echo -e "${RED}&#91;ERROR]${NC} $*" &gt;&amp;2
}

backup_database() {
    local wp_path=$1
    local backup_dir=$2
    local timestamp=$3
    local db_file="${backup_dir}/database_${timestamp}.sql"

    log_info "Backing up database..."

    if wp db export "$db_file" --path="$wp_path" --quiet; then
        gzip "$db_file"
        log_info "Database backed up: ${db_file}.gz"
        echo "${db_file}.gz"
        return 0
    else
        log_error "Database backup failed"
        return 1
    fi
}

backup_files() {
    local wp_path=$1
    local backup_dir=$2
    local timestamp=$3
    local files_archive="${backup_dir}/files_${timestamp}.tar.gz"
    local exclude_opts="--exclude=wp-content/cache --exclude=wp-content/backup*"

    if &#91; "$INCLUDE_UPLOADS" = "false" ]; then
        exclude_opts="${exclude_opts} --exclude=wp-content/uploads"
    fi

    log_info "Backing up files..."

    if tar -czf "$files_archive" $exclude_opts \
        -C "$(dirname $wp_path)" "$(basename $wp_path)" 2&gt;/dev/null; then
        log_info "Files backed up: ${files_archive}"
        echo "$files_archive"
        return 0
    else
        log_error "File backup failed"
        return 1
    fi
}

create_combined_backup() {
    local db_backup=$1
    local files_backup=$2
    local backup_dir=$3
    local timestamp=$4
    local combined="${backup_dir}/wordpress_backup_${timestamp}.tar.gz"
    local temp_dir=$(mktemp -d)

    cp "$db_backup" "$files_backup" "$temp_dir/"
    tar -czf "$combined" -C "$temp_dir" .
    rm -rf "$temp_dir" "$db_backup" "$files_backup"

    log_info "Combined backup: ${combined}"
    echo "$combined"
}

cleanup_old_backups() {
    local backup_dir=$1
    local retention_days=$2

    log_info "Cleaning up backups older than ${retention_days} days..."

    find "$backup_dir" -name "wordpress_backup_*.tar.gz" \
        -type f -mtime +${retention_days} -delete
}

send_notification() {
    local subject=$1
    local message=$2

    if &#91; -n "$NOTIFICATION_EMAIL" ] &amp;&amp; command -v mail &amp;&gt; /dev/null; then
        echo -e "$message" | mail -s "$subject" "$NOTIFICATION_EMAIL"
    fi
}

<em>#############################################</em>
<em># MAIN</em>
<em>#############################################</em>

TIMESTAMP=$(date +"%Y%m%d_%H%M%S")

log_info "========================================="
log_info "WordPress Backup Started"
log_info "========================================="
log_info "Path: ${WP_PATH}"
log_info "Timestamp: ${TIMESTAMP}"

mkdir -p "$BACKUP_DIR"

<em># Backup database</em>
if ! db_backup=$(backup_database "$WP_PATH" "$BACKUP_DIR" "$TIMESTAMP"); then
    send_notification "Backup FAILED" "Database backup failed"
    exit 1
fi

<em># Backup files</em>
if ! files_backup=$(backup_files "$WP_PATH" "$BACKUP_DIR" "$TIMESTAMP"); then
    send_notification "Backup FAILED" "File backup failed"
    exit 1
fi

<em># Create combined backup</em>
final_backup=$(create_combined_backup "$db_backup" "$files_backup" "$BACKUP_DIR" "$TIMESTAMP")

<em># Cleanup old backups</em>
cleanup_old_backups "$BACKUP_DIR" "$RETENTION_DAYS"

<em># Get backup size</em>
backup_size=$(du -h "$final_backup" | cut -f1)

log_info "========================================="
log_info "Backup Completed Successfully!"
log_info "File: ${final_backup}"
log_info "Size: ${backup_size}"
log_info "========================================="

send_notification "Backup SUCCESS" "Backup: ${final_backup}\nSize: ${backup_size}"

exit 0
</code></pre>



<p><strong>Download this script:</strong> 👉 <a href="/#get-started">Get the production-ready backup script free</a></p>



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



<h4 class="wp-block-heading" id="issue-1-wp-command-not-found">Issue 1: &#8220;wp: command not found&#8221;</h4>



<p><strong>Problem</strong>: WP-CLI is not in the cron user&#8217;s PATH.</p>



<p><strong>Solution</strong>: Use full path in cron:</p>



<pre class="wp-block-code"><code><em># Instead of:</em>
0 2 * * * ~/wp-backup.sh

<em># Use:</em>
0 2 * * * /usr/local/bin/wp-backup.sh
</code></pre>



<p>Or add PATH to crontab:</p>



<pre class="wp-block-code"><code>PATH=/usr/local/bin:/usr/bin:/bin
0 2 * * * ~/wp-backup.sh
</code></pre>



<h4 class="wp-block-heading" id="issue-2-permission-denied">Issue 2: Permission Denied</h4>



<p><strong>Problem</strong>: Backup script can&#8217;t write to backup directory.</p>



<p><strong>Solution</strong>:</p>



<pre class="wp-block-code"><code><em># Create directory with proper permissions</em>
sudo mkdir -p /var/backups/wordpress
sudo chown $USER:$USER /var/backups/wordpress
chmod 700 /var/backups/wordpress
</code></pre>



<h4 class="wp-block-heading" id="issue-3-disk-space-full">Issue 3: Disk Space Full</h4>



<p><strong>Problem</strong>: Backups fill up the disk.</p>



<p><strong>Solution</strong>: Reduce retention or exclude uploads:</p>



<pre class="wp-block-code"><code><em># Check disk usage</em>
df -h

<em># Reduce retention to 3 days</em>
RETENTION_DAYS=3

<em># Or exclude uploads</em>
INCLUDE_UPLOADS=false
</code></pre>



<h4 class="wp-block-heading" id="issue-4-backup-taking-too-long">Issue 4: Backup Taking Too Long</h4>



<p><strong>Problem</strong>: Large sites take 30+ minutes to backup.</p>



<p><strong>Solution</strong>:</p>



<ol class="wp-block-list">
<li>Exclude uploads (back up separately)</li>



<li>Run during off-peak hours</li>



<li>Use incremental backups for files</li>



<li>Consider database replication for very large databases</li>
</ol>



<h4 class="wp-block-heading" id="issue-5-cron-job-not-running">Issue 5: Cron Job Not Running</h4>



<p><strong>Problem</strong>: Script works manually but not via cron.</p>



<p><strong>Solution</strong>: Check cron logs and add logging:</p>



<pre class="wp-block-code"><code><em># Add to crontab</em>
0 2 * * * /home/user/wp-backup.sh &gt;&gt; /var/log/wp-backup.log 2&gt;&amp;1

<em># Check logs</em>
tail -f /var/log/wp-backup.log

<em># Verify cron service is running</em>
sudo systemctl status cron
</code></pre>



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



<p>Congratulations! You now have a production-ready WordPress backup automation system.</p>



<h4 class="wp-block-heading" id="enhance-your-backup-system">Enhance Your Backup System</h4>



<ol class="wp-block-list">
<li><strong><a href="#">Schedule WP-CLI Tasks with Cron</a></strong> &#8211; Advanced scheduling techniques</li>



<li><strong><a href="#">WordPress Multisite Network Management</a></strong> &#8211; Back up entire networks</li>



<li><strong><a href="#">WordPress CI/CD Pipeline with GitHub Actions</a></strong> &#8211; Integrate backups into deployment</li>
</ol>



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



<ul class="wp-block-list">
<li><strong><a href="https://file+.vscode-resource.vscode-cdn.net/blog/bash-functions-wordpress-automation">Bash Functions for WordPress</a></strong>&nbsp;&#8211; Make your scripts more maintainable</li>



<li><strong><a href="https://file+.vscode-resource.vscode-cdn.net/blog/error-handling-bash-scripts-wpcli">Error Handling in Bash Scripts</a></strong>&nbsp;&#8211; Handle failures gracefully</li>



<li><strong><a href="https://file+.vscode-resource.vscode-cdn.net/blog/install-configure-wpcli-ubuntu-debian">Install WP-CLI</a></strong>&nbsp;&#8211; Get WP-CLI set up properly</li>
</ul>



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



<p>Want to build advanced automation systems like:</p>



<ul class="wp-block-list">
<li>Automated blog publishing with AI and APIs</li>



<li>Complete WordPress deployment pipelines</li>



<li>Multi-site management at scale</li>
</ul>



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



<ul class="wp-block-list">
<li>This complete backup script (production-ready)</li>



<li>Advanced backup strategies (incremental, differential)</li>



<li>Off-site storage integration templates</li>



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



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



<p>Automated WordPress backups are non-negotiable for any production site. With WP-CLI and Bash, you can build a bulletproof backup system in less than an hour that runs flawlessly for years.</p>



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



<ul class="wp-block-list">
<li>Automate backups to eliminate human error</li>



<li>Backup both database and files</li>



<li>Compress backups to save space</li>



<li>Implement retention policies to manage disk usage</li>



<li>Test restores regularly (untested backups are useless)</li>



<li>Store backups off-site for disaster recovery</li>



<li>Monitor with email notifications</li>
</ul>



<p>The script you built today can protect hundreds of WordPress sites. Set it up once, and sleep better knowing your data is safe.</p>



<p><strong>Ready to implement?</strong>&nbsp;Download the complete production script and get it running today.</p>



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



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



<p><strong>Found this helpful?</strong>&nbsp;Share it with other WordPress developers who need automated backups.</p>



<p><strong>Next:</strong> Learn how to <a href="#">deploy WordPress environments automatically</a> using similar automation techniques.</p>
<p>The post <a href="https://wpclimastery.com/blog/automate-wordpress-backups-with-wp-cli-and-bash-step-by-step/">Automate WordPress Backups with WP-CLI and Bash (Step-by-Step)</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wpclimastery.com/blog/automate-wordpress-backups-with-wp-cli-and-bash-step-by-step/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
