<?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>wp-cli network bulk Archives - WP-CLI Mastery</title>
	<atom:link href="https://wpclimastery.com/blog/tag/wp-cli-network-bulk/feed/" rel="self" type="application/rss+xml" />
	<link>https://wpclimastery.com/blog/tag/wp-cli-network-bulk/</link>
	<description>Automate WordPress Like a DevOps Pro.</description>
	<lastBuildDate>Mon, 24 Nov 2025 11:16:48 +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>wp-cli network bulk Archives - WP-CLI Mastery</title>
	<link>https://wpclimastery.com/blog/tag/wp-cli-network-bulk/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Bulk Operations Across WordPress Multisite Networks with WP-CLI</title>
		<link>https://wpclimastery.com/blog/bulk-operations-across-wordpress-multisite-networks-with-wp-cli/</link>
		
		<dc:creator><![CDATA[Krasen]]></dc:creator>
		<pubDate>Mon, 24 Nov 2025 11:16:25 +0000</pubDate>
				<category><![CDATA[Multisite Management]]></category>
		<category><![CDATA[multisite bulk operations]]></category>
		<category><![CDATA[multisite management]]></category>
		<category><![CDATA[network-wide updates]]></category>
		<category><![CDATA[wordpress multisite automation]]></category>
		<category><![CDATA[wp-cli network bulk]]></category>
		<guid isPermaLink="false">https://wpclimastery.com/?p=216</guid>

					<description><![CDATA[<p>WordPress Multisite networks power some of the web&#8217;s largest publishing platforms, educational institutions, and enterprise content systems. Managing dozens or hundreds of sites individually is impractical. WP-CLI provides essential tools...</p>
<p>The post <a href="https://wpclimastery.com/blog/bulk-operations-across-wordpress-multisite-networks-with-wp-cli/">Bulk Operations Across WordPress Multisite Networks with WP-CLI</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>WordPress Multisite networks power some of the web&#8217;s largest publishing platforms, educational institutions, and enterprise content systems. Managing dozens or hundreds of sites individually is impractical. WP-CLI provides essential tools for executing bulk operations across entire networks efficiently and consistently.</p>



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



<p>WordPress Multisite transforms a single WordPress installation into a network of sites sharing core files, plugins, and themes while maintaining separate databases for content. This architecture enables centralized management but requires specialized tools for network-wide operations.</p>



<p>Each site in a multisite network has a unique site ID, URL, and database tables. Network administrators need methods to iterate through sites, apply configurations selectively, and verify operations across the entire network. WP-CLI&#8217;s multisite commands make these complex operations manageable.</p>



<h2 class="wp-block-heading" id="basic-multisite-navigation">Basic Multisite Navigation</h2>



<p>Before performing bulk operations, understand how to navigate and query your multisite network.</p>



<pre class="wp-block-code"><code><em># List all sites in the network</em>
wp site list

<em># List sites with specific columns</em>
wp site list --fields=blog_id,url,registered

<em># Count total sites</em>
wp site list --format=count

<em># Get specific site details</em>
wp site list --site__in=1,5,10

<em># Filter sites by URL pattern</em>
wp site list --url='*.example.com'

<em># Find archived/deleted/spam sites</em>
wp site list --archived=true
wp site list --deleted=true
wp site list --spam=true
</code></pre>



<p>Get detailed information about a specific site:</p>



<pre class="wp-block-code"><code><em># Get site URL</em>
wp site url 5

<em># Get site domain and path</em>
wp site list --field=url --blog_id=5

<em># Check if site is public</em>
wp option get blog_public --url=example.com/site
</code></pre>



<h2 class="wp-block-heading" id="iterating-through-network-sites">Iterating Through Network Sites</h2>



<p>The foundation of bulk operations is iterating through all sites and executing commands for each.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># Basic site iteration example</em>

<em># Get all site URLs</em>
for url in $(wp site list --field=url); do
    echo "Processing: $url"
    wp --url="$url" option get blogname
done

<em># Using site IDs</em>
for site_id in $(wp site list --field=blog_id); do
    echo "Processing site ID: $site_id"
    SITE_URL=$(wp site url $site_id)
    wp --url="$SITE_URL" plugin list --status=active
done
</code></pre>



<p>Advanced iteration with error handling:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># iterate-sites.sh - Robust site iteration with error handling</em>

set -euo pipefail

LOG_FILE="/var/log/multisite-operations-$(date +%Y%m%d).log"
SUCCESS_COUNT=0
FAILURE_COUNT=0

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

process_site() {
    local url=$1
    log "Processing: $url"

    <em># Execute operation with error handling</em>
    if wp --url="$url" cache flush 2&gt;&amp;1 | tee -a "$LOG_FILE"; then
        ((SUCCESS_COUNT++))
        log "SUCCESS: $url"
    else
        ((FAILURE_COUNT++))
        log "FAILED: $url"
    fi
}

<em># Main execution</em>
log "Starting network-wide operation"

<em># Get all active sites (exclude archived, deleted, spam)</em>
SITES=$(wp site list --archived=false --deleted=false --spam=false --field=url)

TOTAL=$(echo "$SITES" | wc -l)
log "Total sites to process: $TOTAL"

<em># Process each site</em>
echo "$SITES" | while read -r site_url; do
    process_site "$site_url"
done

log "Operation completed. Success: $SUCCESS_COUNT, Failed: $FAILURE_COUNT"
</code></pre>



<h2 class="wp-block-heading" id="network-wide-plugin-management">Network-Wide Plugin Management</h2>



<p>Manage plugins across all sites in your network consistently.</p>



<pre class="wp-block-code"><code><em># Network activate a plugin (available to all sites)</em>
wp plugin install jetpack --activate-network

<em># Network deactivate a plugin</em>
wp plugin deactivate jetpack --network

<em># Check plugin status across all sites</em>
for url in $(wp site list --field=url); do
    echo "Site: $url"
    wp --url="$url" plugin list --status=active --fields=name,version
done

<em># Activate plugin on all sites</em>
for url in $(wp site list --field=url); do
    wp --url="$url" plugin activate woocommerce
    echo "Activated on: $url"
done

<em># Deactivate plugin from specific sites</em>
for url in $(wp site list --field=url); do
    if &#91;&#91; "$url" == *"store"* ]]; then
        echo "Keeping plugin on: $url"
    else
        wp --url="$url" plugin deactivate woocommerce
        echo "Deactivated on: $url"
    fi
done
</code></pre>



<p>Selective plugin deployment based on site characteristics:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># deploy-plugin-conditional.sh - Deploy plugin based on conditions</em>

PLUGIN_SLUG="custom-plugin"
REQUIRED_THEME="storefront"

echo "Deploying $PLUGIN_SLUG to sites using $REQUIRED_THEME theme"

for url in $(wp site list --field=url); do
    <em># Get active theme for site</em>
    ACTIVE_THEME=$(wp --url="$url" theme list --status=active --field=name)

    if &#91; "$ACTIVE_THEME" == "$REQUIRED_THEME" ]; then
        echo "Activating $PLUGIN_SLUG on $url"
        wp --url="$url" plugin activate "$PLUGIN_SLUG"

        <em># Configure plugin settings</em>
        wp --url="$url" option update custom_plugin_enabled 1
    else
        echo "Skipping $url (theme: $ACTIVE_THEME)"
    fi
done

echo "Selective deployment completed"
</code></pre>



<h2 class="wp-block-heading" id="bulk-content-operations">Bulk Content Operations</h2>



<p>Perform content operations across multiple sites simultaneously.</p>



<pre class="wp-block-code"><code><em># Count posts across all sites</em>
for url in $(wp site list --field=url); do
    COUNT=$(wp --url="$url" post list --post_type=post --format=count)
    echo "$url: $COUNT posts"
done

<em># Bulk publish scheduled posts</em>
for url in $(wp site list --field=url); do
    SCHEDULED=$(wp --url="$url" post list --post_status=future --format=count)
    if &#91; "$SCHEDULED" -gt 0 ]; then
        echo "$url has $SCHEDULED scheduled posts"
        wp --url="$url" post list --post_status=future --field=ID | while read -r post_id; do
            wp --url="$url" post update $post_id --post_status=publish
        done
    fi
done

<em># Delete spam comments network-wide</em>
for url in $(wp site list --field=url); do
    SPAM_COUNT=$(wp --url="$url" comment list --status=spam --format=count)
    if &#91; "$SPAM_COUNT" -gt 0 ]; then
        echo "Deleting $SPAM_COUNT spam comments from $url"
        wp --url="$url" comment delete $(wp --url="$url" comment list --status=spam --format=ids) --force
    fi
done
</code></pre>



<p>Network-wide content creation:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># create-default-pages.sh - Create standard pages on all sites</em>

PAGES=(
    "Privacy Policy|privacy-policy|Privacy policy content here"
    "Terms of Service|terms-of-service|Terms of service content here"
    "Contact|contact|Contact information here"
)

for url in $(wp site list --field=url); do
    echo "Creating pages on: $url"

    for page_data in "${PAGES&#91;@]}"; do
        IFS='|' read -r title slug content &lt;&lt;&lt; "$page_data"

        <em># Check if page already exists</em>
        if ! wp --url="$url" post list --post_type=page --name="$slug" --format=ids | grep -q '&#91;0-9]'; then
            POST_ID=$(wp --url="$url" post create \
                --post_type=page \
                --post_title="$title" \
                --post_name="$slug" \
                --post_content="$content" \
                --post_status=publish \
                --porcelain)

            echo "Created '$title' (ID: $POST_ID) on $url"
        else
            echo "'$title' already exists on $url"
        fi
    done
done

echo "Page creation completed"
</code></pre>



<h2 class="wp-block-heading" id="database-maintenance-across-network">Database Maintenance Across Network</h2>



<p>Maintain database health across all sites in the network.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># network-database-maintenance.sh - Database cleanup for all sites</em>

LOG_FILE="/var/log/network-db-maintenance-$(date +%Y%m%d).log"

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

log "Starting network-wide database maintenance"

for url in $(wp site list --field=url); do
    log "Processing: $url"

    <em># Get initial database size</em>
    SITE_ID=$(wp site list --url="$url" --field=blog_id)
    INITIAL_SIZE=$(wp db size --tables="wp_${SITE_ID}_*" --format=json 2&gt;/dev/null || echo "0")

    <em># Delete post revisions older than 90 days</em>
    REVISIONS=$(wp --url="$url" post list --post_type=revision --format=count)
    if &#91; "$REVISIONS" -gt 0 ]; then
        log "  Deleting $REVISIONS revisions"
        wp --url="$url" post delete $(wp --url="$url" post list --post_type=revision --format=ids) --force
    fi

    <em># Clean transients</em>
    log "  Cleaning transients"
    wp --url="$url" transient delete --expired

    <em># Delete orphaned metadata</em>
    log "  Cleaning orphaned metadata"
    wp --url="$url" db query "
        DELETE pm FROM wp_${SITE_ID}_postmeta pm
        LEFT JOIN wp_${SITE_ID}_posts wp ON wp.ID = pm.post_id
        WHERE wp.ID IS NULL
    " 2&gt;/dev/null || true

    <em># Optimize tables</em>
    log "  Optimizing tables"
    wp db optimize 2&gt;/dev/null || true

    log "  Completed: $url"
done

log "Network database maintenance completed"
</code></pre>



<h2 class="wp-block-heading" id="network-wide-configuration-updates">Network-Wide Configuration Updates</h2>



<p>Apply configuration changes across all sites consistently.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># update-network-config.sh - Apply settings network-wide</em>

<em># Configuration to apply</em>
CONFIG=(
    "blog_public|0|Disable search engine indexing"
    "default_comment_status|closed|Close comments by default"
    "timezone_string|America/New_York|Set timezone"
    "date_format|F j, Y|Set date format"
    "time_format|g:i a|Set time format"
)

echo "Applying configuration to all sites..."

for url in $(wp site list --field=url); do
    echo "Configuring: $url"

    for config_line in "${CONFIG&#91;@]}"; do
        IFS='|' read -r option value description &lt;&lt;&lt; "$config_line"

        wp --url="$url" option update "$option" "$value"
        echo "  Set $option = $value ($description)"
    done
done

echo "Configuration applied to all sites"
</code></pre>



<h2 class="wp-block-heading" id="selective-site-operations">Selective Site Operations</h2>



<p>Execute operations on filtered subsets of sites.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># selective-operations.sh - Target specific sites</em>

<em># Operation 1: Update only sites matching URL pattern</em>
echo "Updating sites with 'blog' in URL..."
for url in $(wp site list --field=url | grep 'blog'); do
    echo "Processing: $url"
    wp --url="$url" plugin activate akismet
done

<em># Operation 2: Target sites by registration date</em>
echo "Processing recently created sites..."
CUTOFF_DATE=$(date -d "30 days ago" +%Y-%m-%d)

wp site list --format=json | jq -r --arg date "$CUTOFF_DATE" '.&#91;] |
    select(.registered &gt;= $date) | .url' | while read -r url; do
    echo "Recent site: $url"
    wp --url="$url" post create \
        --post_title="Welcome" \
        --post_content="Welcome to our network!" \
        --post_status=publish
done

<em># Operation 3: Target sites by size</em>
echo "Finding large sites..."
for url in $(wp site list --field=url); do
    POST_COUNT=$(wp --url="$url" post list --format=count)

    if &#91; "$POST_COUNT" -gt 1000 ]; then
        echo "Large site: $url ($POST_COUNT posts)"
        <em># Perform operation for large sites</em>
        wp --url="$url" option update posts_per_page 20
    fi
done
</code></pre>



<h2 class="wp-block-heading" id="user-management-across-network">User Management Across Network</h2>



<p>Manage users and permissions network-wide.</p>



<pre class="wp-block-code"><code><em># Add super admin</em>
wp super-admin add username

<em># Remove super admin</em>
wp super-admin remove username

<em># List all super admins</em>
wp super-admin list

<em># Add user to specific sites</em>
USER_ID=123
for url in $(wp site list --field=url | head -5); do
    wp --url="$url" user set-role $USER_ID editor
    echo "Added user $USER_ID as editor on $url"
done

<em># Find users across all sites</em>
for url in $(wp site list --field=url); do
    USER_COUNT=$(wp --url="$url" user list --format=count)
    echo "$url: $USER_COUNT users"
done
</code></pre>



<p>Network-wide user audit:</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># audit-network-users.sh - Comprehensive user audit</em>

REPORT_FILE="/var/log/network-user-audit-$(date +%Y%m%d).txt"

echo "=== Network User Audit ===" &gt; "$REPORT_FILE"
echo "Date: $(date)" &gt;&gt; "$REPORT_FILE"
echo "" &gt;&gt; "$REPORT_FILE"

<em># Super admins</em>
echo "Super Administrators:" &gt;&gt; "$REPORT_FILE"
wp super-admin list &gt;&gt; "$REPORT_FILE"
echo "" &gt;&gt; "$REPORT_FILE"

<em># Users per site</em>
echo "Users per site:" &gt;&gt; "$REPORT_FILE"
for url in $(wp site list --field=url); do
    ADMIN_COUNT=$(wp --url="$url" user list --role=administrator --format=count)
    EDITOR_COUNT=$(wp --url="$url" user list --role=editor --format=count)
    TOTAL_COUNT=$(wp --url="$url" user list --format=count)

    echo "$url:" &gt;&gt; "$REPORT_FILE"
    echo "  Admins: $ADMIN_COUNT" &gt;&gt; "$REPORT_FILE"
    echo "  Editors: $EDITOR_COUNT" &gt;&gt; "$REPORT_FILE"
    echo "  Total: $TOTAL_COUNT" &gt;&gt; "$REPORT_FILE"
done

echo "Audit completed: $REPORT_FILE"
</code></pre>



<h2 class="wp-block-heading" id="performance-optimization-network-wide">Performance Optimization Network-Wide</h2>



<p>Optimize performance across all network sites.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># optimize-network.sh - Network-wide performance optimization</em>

echo "=== Network Performance Optimization ==="

TOTAL_SITES=$(wp site list --format=count)
CURRENT=0

for url in $(wp site list --field=url); do
    ((CURRENT++))
    echo "&#91;$CURRENT/$TOTAL_SITES] Optimizing: $url"

    <em># Clear expired transients</em>
    wp --url="$url" transient delete --expired

    <em># Clear object cache</em>
    wp --url="$url" cache flush

    <em># Regenerate missing thumbnails</em>
    MISSING=$(wp --url="$url" media regenerate --only-missing --yes --dry-run 2&gt;&amp;1 | grep -o '&#91;0-9]* images' | grep -o '&#91;0-9]*')

    if &#91; "$MISSING" -gt 0 ]; then
        echo "  Regenerating $MISSING thumbnails"
        wp --url="$url" media regenerate --only-missing --yes
    fi

    <em># Update rewrite rules</em>
    wp --url="$url" rewrite flush

    echo "  Completed"
    sleep 1  <em># Avoid overloading server</em>
done

echo "Network optimization completed"
</code></pre>



<h2 class="wp-block-heading" id="monitoring-and-reporting">Monitoring and Reporting</h2>



<p>Generate comprehensive reports about your multisite network.</p>



<pre class="wp-block-code"><code>#!/bin/bash
<em># network-health-report.sh - Generate network health report</em>

REPORT_FILE="/var/reports/network-health-$(date +%Y%m%d).html"

<em># HTML header</em>
cat &gt; "$REPORT_FILE" &lt;&lt; 'EOF'
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;Network Health Report&lt;/title&gt;
    &lt;style&gt;
        body { font-family: Arial, sans-serif; margin: 20px; }
        table { border-collapse: collapse; width: 100%; }
        th, td { border: 1px solid <em>#ddd; padding: 8px; text-align: left; }</em>
        th { background-color: <em>#4CAF50; color: white; }</em>
        .warning { background-color: <em>#ffeb3b; }</em>
        .error { background-color: <em>#f44336; color: white; }</em>
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;WordPress Network Health Report&lt;/h1&gt;
    &lt;p&gt;Generated: $(date)&lt;/p&gt;
EOF

<em># Network statistics</em>
TOTAL_SITES=$(wp site list --format=count)
ACTIVE_SITES=$(wp site list --archived=false --deleted=false --spam=false --format=count)

cat &gt;&gt; "$REPORT_FILE" &lt;&lt; EOF
    &lt;h2&gt;Network Overview&lt;/h2&gt;
    &lt;ul&gt;
        &lt;li&gt;Total Sites: $TOTAL_SITES&lt;/li&gt;
        &lt;li&gt;Active Sites: $ACTIVE_SITES&lt;/li&gt;
        &lt;li&gt;Archived/Deleted/Spam: $((TOTAL_SITES - ACTIVE_SITES))&lt;/li&gt;
    &lt;/ul&gt;

    &lt;h2&gt;Site Details&lt;/h2&gt;
    &lt;table&gt;
        &lt;tr&gt;
            &lt;th&gt;Site URL&lt;/th&gt;
            &lt;th&gt;Posts&lt;/th&gt;
            &lt;th&gt;Users&lt;/th&gt;
            &lt;th&gt;Active Plugins&lt;/th&gt;
            &lt;th&gt;Theme&lt;/th&gt;
        &lt;/tr&gt;
EOF

<em># Site details</em>
for url in $(wp site list --field=url); do
    POST_COUNT=$(wp --url="$url" post list --post_type=post --format=count)
    USER_COUNT=$(wp --url="$url" user list --format=count)
    PLUGIN_COUNT=$(wp --url="$url" plugin list --status=active --format=count)
    THEME=$(wp --url="$url" theme list --status=active --field=name)

    cat &gt;&gt; "$REPORT_FILE" &lt;&lt; EOF
        &lt;tr&gt;
            &lt;td&gt;$url&lt;/td&gt;
            &lt;td&gt;$POST_COUNT&lt;/td&gt;
            &lt;td&gt;$USER_COUNT&lt;/td&gt;
            &lt;td&gt;$PLUGIN_COUNT&lt;/td&gt;
            &lt;td&gt;$THEME&lt;/td&gt;
        &lt;/tr&gt;
EOF
done

<em># HTML footer</em>
cat &gt;&gt; "$REPORT_FILE" &lt;&lt; 'EOF'
    &lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;
EOF

echo "Report generated: $REPORT_FILE"
</code></pre>



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



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



<li><a href="https://wordpress.org/support/article/multisite-network-administration/">WordPress Multisite Administration</a></li>



<li><a href="https://developer.wordpress.org/advanced-administration/multisite/">Multisite Best Practices</a></li>



<li><a href="https://codex.wordpress.org/Create_A_Network">Network Administration Guide</a></li>



<li><a href="https://wordpress.org/support/article/multisite-network-administration/">Multisite Performance Optimization</a></li>
</ul>



<p></p>
<p>The post <a href="https://wpclimastery.com/blog/bulk-operations-across-wordpress-multisite-networks-with-wp-cli/">Bulk Operations Across WordPress Multisite Networks with WP-CLI</a> appeared first on <a href="https://wpclimastery.com">WP-CLI Mastery</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
