Automated WordPress Sites Update Script. Save time and reduce security risks scheduling updates at the best time for your sites, including core, plugin, and theme updates.
- Site Discovery: Finds all WordPress installations in the configured root directory (typically
/var/www/). - Pre-update Health Check: Runs basic health checks using
wp-clibefore updates. - Core Update: Updates WordPress core if an update is available.
- Plugin Update: Updates plugins, skipping any listed in
.wp-exclude-pluginsfor per-site exclusions. - Theme & Language Update: Updates themes and language packs.
- Post-update Health Check: Verifies site health after updates.
- Rollback: If a plugin update fails or breaks the site, attempts rollback using
wp-cli. - Logging: Logs all actions and errors per site and to a central error log.
- Notifications: Sends email alerts for failures and rollbacks.
- Cache Flush: Flushes site caches after updates.
wp-cli.curl.jq: The script relies onjqfor parsing JSON data. Ifjqfails to parse the JSON data, the script may not function as expected. Ensurejqis installed and working correctly.- [Optional] Mail sending configured (for notifications).
- No backups performed (ensure you have a backup solution).
- Plugin rollback only works for plugins in the WordPress.org repository. If a plugin rollback fails, it may leave the site in an unstable state. Ensure you have a backup solution in place to restore the site if a rollback fails.
- No visual regression or browser testing.
- Initial health check: Confirms the site is stable BEFORE any updates.
- Core & DB Updates: Updates WordPress core and its database.
- Plugin Updates:
- Checks for available updates for each plugin.
- Excludes plugins specified in the configuration of each site (e.g., /var/www/domain.com/.wp-exclude-plugins).
- Updates one plugin at a time.
- Performs a health check after each plugin update.
- If health check fails OR update fails, automatically rolls back the plugin to its previous version.
- Sends an email notification upon rollback (your server must be configured to send emails).
- Continues to the next plugin, even if one failed.
- Theme & Language Updates: Updates all themes and language files.
- Cache Clearing: Flushes WordPress object and plugin caches.
-
Place the script and configuration file in a secure location:
sudo cp wordpress-scheduled-updates.sh /usr/local/bin/ sudo cp wordpress-scheduled-updates.conf /etc/
-
Make the script executable and set the ownership to www-data:
sudo chmod +x /usr/local/bin/wordpress-scheduled-updates.sh sudo chown www-data:www-data /usr/local/bin/wordpress-scheduled-updates.sh sudo chown www-data:www-data /etc/wordpress-scheduled-updates.conf # If you are using a custom config file path, be sure set appropriate permissions to it # sudo chown www-data:www-data /usr/local/bin/wordpress-scheduled-updates.conf
-
Create the log directory with appropriate permissions:
sudo mkdir -p /var/log/wordpress-scheduled-updates sudo chown www-data:adm /var/log/wordpress-scheduled-updates sudo chmod 750 /var/log/wordpress-scheduled-updates
-
Set up a cron job (recommended as
www-datauser):sudo crontab -u www-data -e
Then add something like the example below, which schedules a task to run everyday at 3:00 AM:
# Using automatic config detection (/etc/ then script directory) 0 3 * * * /usr/local/bin/wordpress-scheduled-updates.sh >> /var/log/wordpress-scheduled-updates/cron.log 2>&1 # Using custom config file 0 3 * * * /usr/local/bin/wordpress-scheduled-updates.sh /path/to/custom.conf >> /var/log/wordpress-scheduled-updates/cron.log 2>&1The script will execute at that time, and its output (including errors) will be appended to
/var/log/wordpress-scheduled-updates/cron.log. -
(Optional) Exclude specific plugins from updates by creating a
.wp-exclude-pluginsfile in each site directory:# Example: Exclude plugins for example.com sudo nano /var/www/example.com/.wp-exclude-pluginsAdd plugin names (one per line):
woocommerce classic-editor # This is a comment - lines starting with # are ignored custom-plugin-name
The script uses sensible defaults and can optionally load settings from a configuration file. Configuration files are loaded in this priority order:
- Custom config file: Pass any config file path as the first argument to the script
- System config file:
/etc/wordpress-scheduled-updates.conf(checked automatically if no argument provided) - Script directory config:
wordpress-scheduled-updates.confin the same directory as the script - Built-in defaults: Used if no config file is found
Create and edit the configuration file to customize the behavior:
- Site root: Change
SITES_ROOTif your WordPress sites are not in/var/www/. - Log directory: Set
LOG_DIRfor log storage. - Email notifications: Set
NOTIFY_EMAILto your address (leave empty to disable notifications). - Expected user: Set
EXPECTED_USERto match your web server user. - Excluded sites: Add site names to
EXCLUDED_SITESarray. - Included sites: Set
INCLUDED_SITESto process only specific sites (overridesEXCLUDED_SITES). - Skip smoke tests: Set
DISABLE_URL_CHECKS=trueto skip HTTP homepage checks (useful for under-construction or private sites). - Per-site plugin exclusions: Create a
.wp-exclude-pluginsfile in a site's directory, listing plugin names to skip. - Site-specific configs: Create individual config files like
example.com.confwithINCLUDED_SITES="example.com"for single-site updates.
The script uses a two-tiered logging approach:
-
A main cron log file that captures the stdout and stderr of the entire script run. This is what you see in the cron job command.
-
A directory of per-site logs where the script writes detailed, timestamped logs for each WordPress installation it processes. This is what's configured by the $LOG_DIR variable.
You can use logrotate for automatic log rotation. Add the following lines to /etc/logrotate.d/wordpress-scheduled-updates:
# Logrotate configuration for scheduled WordPress updates
# This handles the main cron log and all per-site logs
/var/log/wordpress-scheduled-updates/*.log {
daily
rotate 14
compress
missingok
notifempty
create 640 www-data adm
postrotate
/bin/killall -HUP syslogd
endscript
}
Test in a non-production environment before deploying!
- wp-cli not found: Ensure
wp-cliis installed and in the user's PATH. - Permission errors: Run the script as a user with access to all site directories (typically
www-data). - Email not sent: Check your mail configuration and logs for errors.
- Log file not created: Verify that
$LOG_DIRexists and is writable.
- Implement a manual rollback feature that is independent of wpcli and could be used with any plugin.
Pull requests and suggestions are welcome! Please open issues for bugs or feature requests.
MIT License. See LICENSE for details.
This repository and its documentation were created with the assistance of AI. While efforts have been made to ensure accuracy and completeness, no guarantee is provided. Use at your own risk. Always test in a safe environment before deploying to production.