CloudPanel Sync Script Production to Staging

08/01/2025 08/01/2025 devops 5 mins read
Table Of Contents

Why Automate CloudPanel Synchronization?

Managing multiple WordPress environments is a crucial part of modern web development. Whether you’re testing updates, developing new features, or troubleshooting issues, having an automated synchronization system between your production and staging environments is essential. This guide will walk you through creating and optimizing a robust synchronization script for CloudPanel environments.

Understanding the Synchronization Process

Before diving into the code, let’s understand what our script accomplishes:

  1. Safely exports the production database
  2. Transfers website files while preserving critical configurations
  3. Updates database URLs and settings for the staging environment
  4. Maintains proper permissions and security
  5. Handles cache management

The Synchronization Script

Let’s look at the synchronization script with error handling, logging, and security features:

cloudpanel-sync.sh
#!/bin/bash
# =========================================================================== #
# Description: CloudPanel Sync Script
# Purpose: Synchronize WordPress sites between production and staging
# Environment: CloudPanel (Debian/Ubuntu)
# Requirements: CloudPanel, SSH key authentication
# =========================================================================== #
# Script configuration
set -euo pipefail # Enhanced error handling
trap 'echo "Error on line $LINENO"' ERR
# Configuration variables
declare -A config=(
["PROD_DOMAIN"]="example.com"
["PROD_USER"]="prod-user"
["STAGING_DOMAIN"]="staging.example.com"
["STAGING_USER"]="staging-user"
["REMOTE_SSH"]="root@production-ip"
["TABLE_PREFIX"]="wp_"
)
# Path configurations
declare -A paths=(
["PROD_PATH"]="/home/${config[PROD_USER]}/htdocs/${config[PROD_DOMAIN]}"
["STAGING_PATH"]="/home/${config[STAGING_USER]}/htdocs/${config[STAGING_DOMAIN]}"
["LOG_FILE"]="/home/${config[STAGING_USER]}/sync.log"
)
# Enhanced logging function
log() {
local level=$1
shift
echo "[$(date -u '+%Y-%m-%d %H:%M:%S UTC')] [$level] $*" | tee -a "${paths[LOG_FILE]}"
}
# Validation functions
validate_paths() {
if [[ ! -d "${paths[STAGING_PATH]}" ]]; then
log "ERROR" "Staging directory ${paths[STAGING_PATH]} not found"
exit 1
fi
}
# Database backup function with compression
backup_database() {
log "INFO" "Starting database backup"
ssh "${config[REMOTE_SSH]}" "clpctl db:export --databaseName=${config[PROD_USER]} \
--file=/home/${config[PROD_USER]}/tmp/backup.sql.gz"
}
# Enhanced database sync with progress
sync_database() {
log "INFO" "Synchronizing database"
rsync -azP --info=progress2 \
"${config[REMOTE_SSH]}:/home/${config[PROD_USER]}/tmp/backup.sql.gz" \
"/home/${config[STAGING_USER]}/tmp/"
}
# Main execution flow
main() {
log "INFO" "Starting synchronization process"
# Initial validation
validate_paths
# Backup and sync
backup_database
sync_database
# Update database URLs
log "INFO" "Updating database URLs"
mysql --defaults-extra-file="/home/${config[STAGING_USER]}/my.cnf" \
-D "${config[STAGING_USER]}" -e "
UPDATE ${config[TABLE_PREFIX]}options
SET option_value = REPLACE(option_value,
'https://${config[PROD_DOMAIN]}',
'https://${config[STAGING_DOMAIN]}')
WHERE option_name IN ('home', 'siteurl');"
# File synchronization with exclusions
log "INFO" "Syncing files"
rsync -azP --delete \
--exclude={'wp-config.php','.user.ini','wp-content/cache/*'} \
"${config[REMOTE_SSH]}:${paths[PROD_PATH]}/" \
"${paths[STAGING_PATH]}/"
# Security measures
log "INFO" "Applying security settings"
mysql --defaults-extra-file="/home/${config[STAGING_USER]}/my.cnf" \
-D "${config[STAGING_USER]}" \
-e "UPDATE ${config[TABLE_PREFIX]}options
SET option_value = '0'
WHERE option_name = 'blog_public';"
# Cleanup
log "INFO" "Performing cleanup"
rm -f "/home/${config[STAGING_USER]}/tmp/backup.sql.gz"
log "SUCCESS" "Synchronization completed"
}
# Execute main function
main

Key implementation and Best Practices

Let’s break down the key implementation in this enhanced version:

1. Structured Configuration

We’ve organized configuration variables into associative arrays, making the script more maintainable and easier to update:

Terminal window
declare -A config=(
["PROD_DOMAIN"]="example.com"
["STAGING_DOMAIN"]="staging.example.com"
# ... other configurations
)

2. Enhanced Error Handling

The script now includes proper error trapping and handling:

Terminal window
set -euo pipefail
trap 'echo "Error on line $LINENO"' ERR

3. Robust Logging

A dedicated logging function provides consistent and detailed logging:

Terminal window
log() {
local level=$1
shift
echo "[$(date -u '+%Y-%m-%d %H:%M:%S UTC')] [$level] $*" | tee -a "${paths[LOG_FILE]}"
}

Common Scenarios and Solutions

Scenario 1: Large File Transfers

When dealing with large WordPress installations:

Terminal window
# Add these rsync options for large transfers
rsync -azP --partial --partial-dir=.rsync-partial \
--exclude={'wp-config.php','.user.ini','wp-content/cache/*'} \
"${config[REMOTE_SSH]}:${paths[PROD_PATH]}/" \
"${paths[STAGING_PATH]}/"

Scenario 2: Custom Plugin Configurations

For sites with specific plugin configurations:

Terminal window
# Add to the database update queries
mysql --defaults-extra-file="/home/${config[STAGING_USER]}/my.cnf" \
-D "${config[STAGING_USER]}" -e "
UPDATE ${config[TABLE_PREFIX]}options
SET option_value = REPLACE(
option_value,
'production-specific-setting',
'staging-specific-setting'
)
WHERE option_name LIKE '%plugin_setting%';"

Scenario 3: Handling Multiple Sites

For managing multiple WordPress installations:

Terminal window
# Create a sites configuration file
cat > sites.conf << EOF
site1:example1.com:user1
site2:example2.com:user2
EOF
# Modified script to handle multiple sites
while IFS=: read -r site domain user; do
config["PROD_DOMAIN"]=$domain
config["PROD_USER"]=$user
main
done < sites.conf

Best Practices for Production Use

  1. Always Test First: Run the script on a test environment before implementing in production.
  2. Backup Strategy: Implement proper backup procedures:
    Terminal window
    # Add to the script before main sync
    backup_date=$(date +%Y%m%d_%H%M%S)
    clpctl backup:create --name="pre_sync_${backup_date}"
  3. Security Considerations: Use SSH keys and restrict permissions:
    Terminal window
    chmod 600 /home/${config[STAGING_USER]}/my.cnf
    chmod 700 /home/${config[STAGING_USER]}/tmp

Monitoring and Maintenance

To ensure your synchronization system remains reliable:

  1. Log Rotation: Implement log rotation to manage log files:

    Terminal window
    # Add to crontab
    0 0 * * * logrotate -f /etc/logrotate.d/cloudpanel-sync
  2. Health Checks: Add post-sync validation:

    Terminal window
    # Add to the main function
    check_wordpress_health() {
    curl -sL "https://${config[STAGING_DOMAIN]}/wp-admin" | grep -q "wp-login"
    return $?
    }

Conclusion

This synchronization script provides a robust foundation for managing WordPress environments in CloudPanel. By implementing and following the best practices, you can ensure reliable and efficient synchronization between your production and staging environments.

Remember to always test the script in a controlled environment first and maintain proper backups before running any synchronization operations. The script can be further customized based on your specific needs and requirements.

For more advanced usage and customization options, open a discussion on our forum https://forum.hhf.technology/.