
Table of Contents
Set core minor updates to automatic, keep major core and plugins behind a staged pipeline, and force every environment to write an update log you can read. You know the policy works when wp core version matches the expected branch, wp plugin list shows only approved versions, failed updates restore the previous build via your rollback script, and your log channel receives a structured entry per attempt. No surprises, no silent breakage.
A controlled policy means you never accept all-or-nothing. In practice this is implemented by environment-level constants, WP-CLI filters and a small mu-plugin that hooks auto_update_$type. Teams that need this wired into a delivery pipeline can use Custom WordPress Development to codify the rules per client.
Core minor vs plugins
WordPress treats core minor releases (for example 6.7.1 to 6.7.2) as safe by default: security and maintenance patches inside the same branch. Plugins have no such guarantee because third-party code can change behaviour between point releases. Your policy must separate them at the configuration layer rather than trusting the checkbox UI.
In wp-config.php set explicit intent:
define( 'WP_AUTO_UPDATE_CORE', 'minor' );
This allows automatic minor core updates only. To take finer control, create wp-content/mu-plugins/update-policy.php and filter per type:
add_filter( 'auto_update_core', '__return_true', 1 );
add_filter( 'auto_update_plugin', 'runok_controlled_plugin_updates', 10, 2 );
function runok_controlled_plugin_updates( $update, $item ) {
$allowlist = array( 'wordfence', 'wp-super-cache' );
return in_array( $item->slug, $allowlist, true );
}
Verify with wp plugin list --field=name,version,update and wp core version. The pitfall is assuming the admin screen mirrors this: a user can still click update, so pair it with define( 'DISALLOW_FILE_MODS', false ) only where intended, and use role caps to remove update_plugins from non-admin roles. If a plugin auto-updates unexpectedly, roll back by restoring the previous version folder from your artifact store and running wp plugin deactivate <slug> && wp plugin activate <slug>.
When to update immediately
Immediate updates are reserved for security releases with a published CVE affecting your installed branch, or a hotfix from a vendor you have pre-approved. Do not wait for a weekly window when the risk is active exploitation.
Decide with a simple rule: if the release notes contain “security” or “hardening” and the component is internet-facing, push within one hour. Everything else goes to the staging pipeline.
Execute immediately with WP-CLI rather than the browser so you capture output:
wp core update --minor
wp plugin update wordfence --no-color 2>&1 | tee -a /var/log/wp/update.log Get a Quote
Email + requirement + budget range — we reply with a quote within 24 hours.
Verify the result: wp core check-update should return empty for the branch, and wp plugin get wordfence --field=version should match the fixed version. Common pitfall: updating on production before the same code exists in version control. Always commit the resulting state or your next deploy will revert silently. If the emergency patch breaks the site, see the WordPress Broken After Update Fix in 2026 runbook to restore service quickly.
Auto-rollback on failure
An auto-update policy is incomplete without automatic restoration. WordPress itself does not roll back core or plugins, so you implement it with a pre-update snapshot and a health check.
Snapshot before the update
Archive the current state so a failure has something to return to:
tar -czf /backup/wp-$(date +%F-%H%M).tar.gz -C /var/www/html wp-content wp-includes wp-admin index.php wp-config.php
Post-update health probe
Immediately after applying, hit a canary endpoint and check status:
HTTP=$(curl -s -o /dev/null -w '%{http_code}' https://example.com/healthz)
if [ "$HTTP" != "200" ]; then
tar -xzf /backup/wp-latest.tar.gz -C /var/www/html
wp cache flush
fi
Verify rollback worked by re-running the curl probe and confirming wp option get siteurl plus a loaded page render. The pitfall is relying on the database alone: plugin updates can change schema, so your snapshot must include a database dump or use a filesystem-level restore point that excludes wp-content/uploads to save time. If rollback itself fails, investigate memory limits first, since a fatal during restore often looks like corruption but is really exhaustion, covered in the WordPress Memory Limit Exhausted Fix 2026 guide.
The Step-by-Step Update Policy Process
- Define allowlists and core policy in
wp-config.phpand a mu-plugin. - Snapshot filesystem and database before any automatic or manual run.
- Execute the update via WP-CLI with output captured to a log file.
- Run health probes against a canary URL and key admin endpoints.
- Trigger rollback from the snapshot if any probe fails or PHP errors appear.
- Emit a structured log entry and notification with the final state.
Logging and notifications
Without logs you cannot prove the policy ran. WordPress writes update attempts to wp-content/debug.log when WP_DEBUG_LOG is enabled, but that is not enough for production because it mixes notices with errors and rotates unpredictably.
Enable dedicated logging:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', '/var/log/wp/wp-debug.log' );
define( 'WP_DEBUG_DISPLAY', false );
Then hook completion events to your own channel. Use the upgrader_process_complete action:
add_action( 'upgrader_process_complete', function( $upgrader, $data ) {
error_log( sprintf(
'UPDATE type=%s action=%s items=%s time=%s',
$data['type'],
$data['action'],
implode(',', array_column( (array) $data['plugins'] ?? [], 'slug' ) ),
gmdate('c')
), 3, '/var/log/wp/update-audit.log' );
}, 10, 2 );
Verify by tailing /var/log/wp/update-audit.log after a test update; you should see one line per component. Pipe that file to your notification stack with a log shipper or a cron that greps for type=plugin and posts to Slack. The pitfall is logging to a path the web user cannot write, which silently drops entries; confirm permissions with ls -l /var/log/wp/. If the site instead returns a blank page after logging changes, consult the WordPress White Screen of Death Fix vendor guide.
Comparison: policy dimensions and where each applies

| Dimension | Automatic | Staged / manual | Best for |
|---|---|---|---|
| Core minor | Yes | No | Security patches, same branch |
| Core major | No | Yes | Branch upgrades like 6.7 to 6.8 |
| Trusted plugins | Yes if allowlisted | No | Security tools with stable releases |
| Custom plugins | No | Yes | Client-specific or untested code |
| Themes | No | Yes | Child themes and layout risk |
Selection advice: use automatic for core minor and allowlisted security plugins only; use staged for everything else including all themes and major core. This is not “it depends” — it is a fixed mapping from component type to pipeline.
Action checklist
- Set
WP_AUTO_UPDATE_COREto'minor'in every environment’s config. - Deploy the mu-plugin allowlist and verify with
wp plugin list. - Enable
/var/log/wp/update-audit.logand confirm the web user can write it. - Add the tar snapshot and health probe to your update cron.
- Map each component type in the table above to automatic or staged and document it.
Need this policy implemented for your fleet? Our team builds audited update pipelines with rollback and logging built in. See our services to start.
Frequently asked questions
How do I know if core minor auto-updates are active?
Run wp core version and compare with the latest minor in your branch, then check wp config get WP_AUTO_UPDATE_CORE. If it returns 'minor' and the version matches, the policy is live.
What happens if a plugin update breaks the site?
Your snapshot restores the previous files and the health probe confirms recovery. Without a snapshot you must reinstall the prior version manually from your artifact store.
Can I log updates without WP_DEBUG?
Yes, the upgrader_process_complete hook writes to your custom audit log independent of WP_DEBUG, as shown in the logging section.
