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

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

<image>
	<url>https://wpclimastery.com/wp-content/uploads/2025/11/cropped-favicon-32x32.webp</url>
	<title>wordpress devops Archives - WP-CLI Mastery</title>
	<link>https://wpclimastery.com/blog/tag/wordpress-devops/</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>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>
