<?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>automated wordpress backups Archives - WP-CLI Mastery</title>
	<atom:link href="https://wpclimastery.com/blog/tag/automated-wordpress-backups/feed/" rel="self" type="application/rss+xml" />
	<link>https://wpclimastery.com/blog/tag/automated-wordpress-backups/</link>
	<description>Automate WordPress Like a DevOps Pro.</description>
	<lastBuildDate>Wed, 12 Nov 2025 20:28:28 +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>automated wordpress backups Archives - WP-CLI Mastery</title>
	<link>https://wpclimastery.com/blog/tag/automated-wordpress-backups/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<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>
