01 Generic first
A feature lands here only if it helps any WordPress site on Upsun. Site-specific behavior belongs in your project, via public filters.
WordPress, running like it's 2026
A single must-use plugin that teaches WordPress everything about the platform it runs on:
environment awareness, router-cache friendliness, read-only-filesystem UX, Site Health
checks that understand Upsun, and a wp upsun CLI command.
Off-platform it fully no-ops — local dev and CI need no special-casing.
$ composer require artetecha/upsun-wp
Real subcommands. Real checks. See the CLI →
Why bother
Traditional WordPress operations were designed for a single mutable server. Upsun gives you immutable builds, instant production clones, and config from the environment — this plugin closes the gap so WordPress actually behaves that way.
git push → build → deploy. Immutable, repeatable, one-command rollback.wp-config.php, copied around by hand.PLATFORM_* directly and never defines constants — your wp-config.php stays the single owner.Cache-Control headers drive the platform router’s cache. wp upsun cache-check explains any page’s verdict.wp upsun migrate. A failure aborts the deploy before traffic switches.Design principles
A feature lands here only if it helps any WordPress site on Upsun. Site-specific behavior belongs in your project, via public filters.
Off-platform — local, CI — the plugin loads and does nothing. Malformed platform data degrades silently, never fatals.
The plugin never defines configuration constants. It reads env and constants, and fills runtime-behavior gaps only.
No feature that pretends an API exists. Limitations are documented instead of papered over — see the router-cache purge story in the roadmap.
PHPUnit against WP stubs — no WordPress install required. Thirteen test suites, one per module or subsystem, on a PHP 8.1→8.4 CI matrix.
Flagship module
Upsun previews are byte-for-byte clones of production — including live payment keys, webhook URLs, and CRM credentials. Click around a naive clone and it will happily email real customers and notify real fulfillment systems.
SafePreviews neuters live outbound integrations on every preview, at runtime, from boot — no DB writes, nothing to undo:
wp_mail is intercepted (or redirected to an inbox you choose) on every preview request.upsun_safe_previews_actions, and one-time DB scrubbing hooks into upsun_preview_sanitize.hooks:
post_deploy: |
wp upsun sanitize --if-needed
One line catches every clone and every data re-sync — post_deploy
is the only hook that runs on both. Fresh clones are detected via an environment stamp;
already-sanitized previews no-op; production just refreshes the stamp.
Opt-in DB sanitizers (all disabled by default): anonymize user emails, anonymize passwords, deactivate listed plugins, scrub listed options — declared once, in versioned config, applied identically to every child environment.
Eleven modules
Each module is independently switchable — a UPSUN_DISABLE_* constant in
wp-config.php or the upsun_mu_modules filter — and ships with
its own unit tests.
environment-indicator
Color-coded admin-bar badge (branch · environment type) with a Console link, a dashboard widget, and a matching login-screen banner.
page-cache
Emits Cache-Control: public, s-maxage={ttl} on anonymous, session-free views so the router can cache them; optionally strips cookie spoilers. Commerce bypass patterns come from the integrations layer.
safe-previews
Mail interception, payment test mode, webhook pausing — live integrations neutered at boot on every preview. The full story ↑
updates-policy
Disables the in-app auto-update machinery (Composer is the update path), replaces the toggles with a note, and removes core Site Health tests that would fail by design.
site-health
Object-cache round-trip, cron configuration, writable mounts, preview search visibility — in Site Health, plus an “Upsun” section in the Info tab. Same registry powers wp upsun doctor.
dashboard
Top-level “Upsun” page built from real core meta boxes — collapsible, draggable, per-user layout. Environment, services, health, caching, module status. Actions, not settings: configuration stays in code.
cron-heartbeat
A recurring event stamps a timestamp; staleness (2× schedule warns, 4× fails) and overdue-event counts surface in Site Health, the dashboard, and wp upsun doctor.
preview-protection
X-Robots-Tag: noindex, nofollow and robots meta on non-production — without touching blog_public, because that database is a production clone.
smtp
Points PHPMailer at the on-platform relay (PLATFORM_SMTP_HOST) unless a mailer plugin already configured SMTP.
writable-paths
Integrations declare where plugins write; the check compares that against your declared mounts, and wp upsun mounts prints ready-to-paste YAML for anything missing. Advisory by design — on Upsun the fix is a mount.
mount-usage
Live disk total/free from the mount filesystem — warns at 80% used, fails at 95%, because a full mount is a rude way to discover a quota. A per-mount size breakdown, computed daily, lands in a “Disk & mounts” dashboard panel.
wp upsun
Deploy-hook friendly: meaningful exit codes, machine-readable
--format=json|yaml|csv, and every command prints
“Not running on Upsun.” and exits 0 off-platform.
$ wp upsun cache-check /shop/ --cookie="tracking=1"
Verdict for /shop/:
cacheable — s-maxage=600
Set-Cookie spoilers ..... none
Bypass patterns ......... no match
Router fetch ............ HIT (age 214s)
wp upsun info
Project, environment, branch, and routes at a glance.
wp upsun doctor
Runs every health check; exits non-zero on failure — wire it into your deploy hook as a gate.
wp upsun relationships
Services this app can talk to — credentials never printed. --health probes each one live: MySQL ping, Redis INFO, HTTP/cluster status.
wp upsun cache flush
Flushes the object cache — and is honest that the router cache has no purge API.
wp upsun cache-check <url>
Answers the #1 WordPress-on-Upsun support question — “why isn’t my page cached?” — with an itemized verdict.
wp upsun mounts
Declared mounts, plus ready-to-paste YAML for every writable path your plugins need but don’t have.
wp upsun migrate
Applies pending deploy migrations in order, once per database. --dry-run to preview; a failure aborts the deploy.
wp upsun sanitize
Fires preview sanitization (refuses on production). --if-needed is stamp-aware and safe on every environment.
Plays well with others
Everything the plugin knows about a specific third-party plugin lives in one class — and contributes exclusively through the same public filters you can use. Every built-in integration is proof the public API is sufficient. Dormant no-ops when the target plugin is absent.
Cart-cookie cache bypasses, checkout cache skips, Stripe test-mode forcing, webhook pausing, writable-path declarations.
Opt-in, DB-writing, idempotent — all disabled by default. Enable them
per-project in the post_deploy hook so the policy is versioned and applied
identically to every child environment.
anonymize-user-emails → user-{ID}@upsun-preview.invalidanonymize-user-passwords — one password, or a {ID} templatedeactivate-plugins — your list, previews onlyscrub-options — options or dotted sub-keys, e.g. gateway.live_secret_keyEvery serious WP-on-Upsun project reinvents this — so it’s in the box. Drop ordered files in a directory, each returning a callable:
20260701_0001_switch_smtp_plugin.php
20260708_0001_seed_new_terms.php
Run by wp upsun migrate in the deploy hook. Completion markers live in the
database, so clones don’t re-run them — and the first failure exits non-zero and aborts
the deploy before traffic switches.
Get started
For any Composer-managed WordPress. The Packagist release
(artetecha/upsun-wp) lands with the in-progress extraction to its own
repo; until then the package is consumed as a Composer path repository — the steps
don’t change.
{
"require": {
"artetecha/upsun-wp": "*"
},
"extra": {
"installer-paths": {
"wordpress/wp-content/mu-plugins/{$name}": ["type:wordpress-muplugin"]
}
}
}
WordPress doesn’t scan mu-plugin subdirectories, so copy the shim to the mu-plugins root at build time:
"post-install-cmd": [
"cp wordpress/wp-content/mu-plugins/upsun/upsun-loader.php wordpress/wp-content/mu-plugins/upsun-loader.php"
]
One line makes every clone and data re-sync detectable and sanitized. Safe on every environment.
hooks:
post_deploy: |
wp upsun sanitize --if-needed
That’s it. On Upsun, eleven modules light up. On your laptop and in CI, the plugin loads and does nothing — zero special-casing, zero config files to fake.
coming soon Companion repo
A starter template repo is in the works: a complete WordPress-on-Upsun project skeleton with everything on this page already wired. Clone it, push it, and you have a running WordPress with previews, caching, health checks, and safe clones from commit one.
.upsun/config.yaml — app, services, mounts, and hooks preconfiguredwp-config.php — credentials, URLs, and salts from the platformpost_deploy hook already wiredRoadmap
wp upsun sanitizewp upsun cache-checkwp upsun mountswp upsun migratewp upsun relationships --health) + mount-usage module. v0.3 complete.github.com/artetecha/upsun-wp + Packagist release — in progress; the v0.3 trigger has firedwp upsun vendor exports installed premium plugins as Composer-ready packages, and reports their pending updatesUpsun\purge_paths()) with pluggable backends for Fastly/Cloudflare-in-front
setups — lighting up natively if the platform ever ships purging.
upsun/wp-ms-dbu until there’s demand.