Maintaining a WordPress site can be cumbersome if you have multiple commitments and are short on time. Updation, optimization, backups, and malware scanning are some of the core tasks associated with any CMS-powered website. Doing it manually takes a considerable amount of time. And if you miss it, it can impact the site's performance. A simple remedy to this problem is to automate the entire process. And that's what we are going to learn in this step-by-step tutorial. I'll show you how to use bash scripting and the WP-CLI tool to automate core WordPress maintenance tasks. Let's get started!

Do not apply the script directly on the production server. Test it once in a staging environment and then transfer it to the production web server. It'll ensure everything works smoothly without issues.
Familiarity with the Linux command-line environment is required to apply this solution. Before running this script for the first time, manually take a backup of the website. After that, it'll be done automatically.
Step 1: Install WP-CLI
Login (SSH) to your VPS and install WP-CLI to kickstart the process.
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
Once the installation is complete, you can verify it through the following command:
wp --info
The core tool has been installed and now it's time to work on the maintenance script.
Step 2: Plan and Structure the Maintenance Script
Before writing the script let's tabulate the maintenance tasks we want it to complete. This way, we can easily create dedicated functions so we complete every task. Here we go!
- Core Updates: This one is a no-brainer. Getting WordPress core updates and security patches in time is one of the critical maintenance tasks to keep your website up-to-date and secure.
- Plugin Updates: Checking plugin compatibility and updating them with the latest stable releases.
- Theme Updates: Similarly, fetching and updating themes is essential to get new features and bug fixes.
- Database Optimization: Regularly removing spam comments, unnecessary post revisions, and general table optimization is essential to keep the database healthy and in good condition.
- Backups: Taking regular backups is essential to ensure smooth recovery if things go bonkers.
- Security Scans: A regular scan to detect backdoors and malware is essential to keep the site secure.
- Performance Optimization: Automating both the optimization of images as well as cache clearance helps keep the website in optimized condition.
- Health Checks: Regular checks of the website's uptime and general statistics help you identify bottlenecks in time.
Now that we know about the core maintenance tasks at hand, it's time to start the scripting process.
Step 3: Build Your First Automation Script
We'll start creating our script with the most essential and core tasks associated with WordPress maintenance. Later on, we'll expand it further to accommodate more advanced and complex tasks.
Let's name the script maintain-wp.sh
which can be changed as per your preferences.
Open the script file in your favorite text editor and start the following primary configuration settings.
#!/bin/bash
# Configuration
SITE_PATH="/var/www/html/wordpress"
BACKUP_DIR="/backups/wordpress"
LOG_FILE="/var/log/maintain-wp.log"
DATE=$(date '+%Y-%m-%d_%H-%M-%S')
# Function to log messages
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
We start with specifying the path to WordPress installation, the directory for the log file, and the path for the backup files. We've also created a basic logging function to troubleshoot issues, if any.
All the script code given below will be appended to the script we've stated above.
Let's move ahead by adding more core functions to this script.
# Function to check if WP-CLI is working
check_wp_cli() {
cd "$SITE_PATH" || exit
if ! wp core version &>/dev/null; then
log_message "ERROR: WP-CLI not working or WordPress not found"
exit 1
fi
}
Next, we have an optional function to check if WP-CLI exists on your Linux system. This function also checks for the WordPress installation. If you are sure about both things, you can skip this function.
# Function to create a backup
create_backup() {
log_message "Creating backup..."
mkdir -p "$BACKUP_DIR"
# Database backup
wp db export "$BACKUP_DIR/database_$DATE.sql"
# Files backup
tar -czf "$BACKUP_DIR/files_$DATE.tar.gz" -C "$(dirname "$SITE_PATH")" "$(basename "$SITE_PATH")"
log_message "Backup completed successfully"
}
The next function takes a backup of both the database and files. Keep in mind, that relying on local backups alone is not a good idea. Always keep a backup on remote storage to ensure reliable site restoration.
# Function to update WordPress core
update_core() {
log_message "Checking for WordPress core updates..."
if wp core check-update | grep -q "WordPress is up to date"; then
log_message "WordPress core is already up to date"
else
log_message "Updating WordPress core..."
wp core update
wp core update-db
log_message "WordPress core updated successfully"
fi
}
This one updates WordPress core CMS files.
# Function to update plugins
update_plugins() {
log_message "Updating plugins..."
# Get the list of plugins with updates available
PLUGINS_TO_UPDATE=$(wp plugin list --update=available --format=csv --fields=name | tail -n +2)
if [ -z "$PLUGINS_TO_UPDATE" ]; then
log_message "All plugins are up to date"
else
log_message "Updating plugins: $PLUGINS_TO_UPDATE"
wp plugin update --all
log_message "Plugins updated successfully"
fi
}
The next function keeps your plugins up to date. If no plugin updates are required, appropriate logging is done.
# Function to optimize database
optimize_database() {
log_message "Optimizing database..."
# Remove spam comments
wp comment delete $(wp comment list --status=spam --format=ids) --force
# Remove post revisions (keep last 3)
wp post delete $(wp post list --post_type=revision --format=ids | head -n -3) --force
# Optimize database tables
wp db optimize
log_message "Database optimization completed"
}
This is an extremely important function and must be part of your WordPress maintenance routine. It optimizes the database by removing spam comments, purging unnecessary post revisions, and general table optimization.
# Main execution
main() {
log_message "Starting WordPress maintenance script"
check_wp_cli
create_backup
update_core
update_plugins
optimize_database
log_message "WordPress maintenance completed successfully"
}
# Execute main function
main "$@"
And finally, the main function executes all these functions one by one in a definite order. Make sure to keep the execution sequence the same as given above.
Step 4: Add Advanced Automation Features
Now that the core functions have been implemented, it's time to add some advanced maintenance routines to this script. It'll make it, a complete WordPress maintenance program.
Multi-Site Management
In case you are running a multi-site WordPress instance, here's how to modify the script.
#!/bin/bash
# Configuration
SITES=(
"/var/www/site1.com"
"/var/www/site2.com"
"/var/www/site3.com"
)
BACKUP_ROOT="/backups"
LOG_FILE="/var/log/wp-multi-maintenance.log"
# Function to maintain a single site
maintain_site() {
local site_path="$1"
local site_name=$(basename "$site_path")
log_message "Starting maintenance for $site_name"
cd "$site_path" || return 1
# Check if WordPress exists
if ! wp core version &>/dev/null; then
log_message "WARNING: WordPress not found in $site_path"
return 1
fi
# Create a site-specific backup directory
local backup_dir="$BACKUP_ROOT/$site_name"
mkdir -p "$backup_dir"
# Perform maintenance tasks
create_backup "$backup_dir"
update_core
update_plugins
optimize_database
log_message "Maintenance completed for $site_name"
}
# Main execution for multiple sites
for site in "${SITES[@]}"; do
maintain_site "$site"
done
During initial configuration, specify the paths to all the websites. Notice how all the sites are processed via a loop.
Error Handling and Notifications
What if the script fails during execution for some reason? We'll add an error handling and email notification system so that one can know where it failed and the error message associated with it.
# Enhanced error handling
set -euo pipefail
# Email configuration
ADMIN_EMAIL="admin@yourwebsite.com"
SMTP_SERVER="your-smtpserver.com"
# Function to send email notification
send_notification() {
local subject="$1"
local message="$2"
echo "$message" | mail -s "$subject" "$ADMIN_EMAIL"
}
# Function to handle errors
error_handler() {
local line_number="$1"
local error_code="$2"
local error_message="WordPress maintenance script failed at line $line_number with exit code $error_code"
log_message "ERROR: $error_message"
send_notification "WordPress Maintenance Failed" "$error_message"
exit "$error_code"
}
# Set up error trap
trap 'error_handler ${LINENO} $?' ERR
Provide the actual values for ADMIN_EMAIL
and SMTP_SERVER
variables. Feel free to edit the error message structure as per your need.
Performance Monitoring
A basic performance monitoring routine can log all the essential parameters that can be checked later to detect any large deviations from the expected values.
# Function to check site performance
check_performance() {
log_message "Checking site performance..."
# Check site loading time
local load_time=$(curl -o /dev/null -s -w '%{time_total}' "https://yourwebsite.com")
log_message "Site load time: ${load_time}s"
# Check database size
local db_size=$(wp db size --format=csv | tail -n +2 | cut -d',' -f2)
log_message "Database size: $db_size"
# Check file system usage
local disk_usage=$(du -sh "$SITE_PATH" | cut -f1)
log_message "Site disk usage: $disk_usage"
}
You can easily filter out these log messages through the grep
utility for analysis.
Step 5: Add Security Enhancements
No maintenance routine is complete until you perform important security checks. It'll ensure your website is safe from malicious software and intruders. Here's a collection of functions to be included in your script.
# Function to perform security checks
security_scan() {
log_message "Performing security scan..."
# Check for suspicious files
find "$SITE_PATH" -name "*.php" -type f -exec grep -l "eval\|base64_decode\|exec\|system" {} \; > /tmp/suspicious_files.txt
if [ -s /tmp/suspicious_files.txt ]; then
log_message "WARNING: Suspicious files found:"
cat /tmp/suspicious_files.txt | tee -a "$LOG_FILE"
send_notification "Security Alert" "Suspicious files detected on WordPress site"
fi
# Check file permissions
find "$SITE_PATH" -type f -perm 777 > /tmp/insecure_files.txt
if [ -s /tmp/insecure_files.txt ]; then
log_message "WARNING: Files with insecure permissions (777) found"
# Fix permissions
find "$SITE_PATH" -type f -perm 777 -exec chmod 644 {} \;
log_message "File permissions corrected"
fi
# Update .htaccess security rules
update_htaccess_security
}
# Function to update .htaccess security
update_htaccess_security() {
local htaccess_file="$SITE_PATH/.htaccess"
# Add security headers if not present
if ! grep -q "X-Content-Type-Options" "$htaccess_file"; then
cat >> "$htaccess_file" << 'EOF'
# Security Headers
<IfModule mod_headers.c>
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
</IfModule>
EOF
log_message "Security headers added to .htaccess"
fi
}
These functions check for suspicious files, check for file permissions, and ensure essential security headers are present in the .htaccess
file.
Step 6: Scheduling Script Execution
Now comes one of the most important steps of scheduling the maintenance script for automatic execution at predefined intervals. There are two ways to do it. Let's take a look at both the methods.
Scheduling through Cron
Here's how you can schedule the script through a cron job:
# Edit crontab
crontab -e
# Add these lines for different scheduling options:
# Daily at 1 AM
0 1 * * * /path/to/your/maintain-wp.sh
# Weekly on Sunday at 2 AM
0 2 * * 0 /path/to/your/maintain-wp.sh
# Monthly on the 1st at 3 AM
0 3 1 * * /path/to/your/maintain-wp.sh
Depending on your requirements, you can add all three or selective lines in your crontab file.
Scheduling through Systemd Timers
A more advanced scheduling system involves the use of systemd
timers. Let's see how to use it.
# Create a timer file: /etc/systemd/system/maintain-wp.timer
[Unit]
Description=WordPress Maintenance Timer
Requires=maintain-wp.service
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
# Create a service file: /etc/systemd/system/maintain-wp.service
[Unit]
Description=WordPress Maintenance Service
After=network.target
[Service]
Type=oneshot
ExecStart=/path/to/your/maintain-wp.sh
User=www-data
Group=www-data
# Enable and start the timer
sudo systemctl enable wp-maintenance.timer
sudo systemctl start wp-maintenance.timer
This is a daily script execution setup that can be modified as per your needs. Make sure to modify the paths and service/timer names as per your preferences.
Conclusion
Automating WordPress maintenance with Bash and WP-CLI can significantly reduce the time and effort required to keep your sites running smoothly.
The scripts and techniques covered in this post provide a solid foundation for building your automated maintenance system. Start implementing these automation techniques today, and you'll soon wonder how you ever managed WordPress sites manually.