wp-env Without Docker: What to Test Locally and on Staging
Try the experimental wp-env PHP runtime for WordPress plugin smoke tests, then verify SQL, HTTPS and mail on a staging stack that matches your release.
RB
Suppose you’re changing a WordPress contact-form plugin. You want to catch a broken field or a missing confirmation before opening a pull request. The form also saves a record and sends an email, though, and those two operations will run on someone else’s server. How much should a green check on your laptop count for?
Use the experimental PHP runtime in wp-env for a quick local smoke test. Keep a separate release check for the database, web server and services your site actually uses. The November 4 changelog for @wordpress/env 12.1.0 introduces this option. It runs WordPress using PHP on your machine, PHP’s built-in web server and SQLite, without Docker. That makes it useful for short feedback loops. It also changes what your test result can tell you.
Start with a disposable plugin checkout
Use a fresh checkout of a trusted single-site plugin and synthetic data. The package needs Node.js 22.13 or later, and the WordPress handbook’s PHP runtime instructions require PHP on your path with pdo_sqlite. Keep Git available for fetching configured sources. Run these commands from the plugin project, which should already have a package.json. The pinned dependency keeps this example tied to the release that introduced the runtime.
node --version
php --version
php --ri pdo_sqlite
npm install --save-dev --save-exact @wordpress/env@12.1.0
npm exec --no -- wp-env start --runtime=phpIf the PHP extension check fails, enable it in the CLI PHP installation used by this terminal and rerun the check. If the project already has a Docker-based wp-env, work in a separate checkout rather than resetting an environment with fixtures you need. wp-env remembers the runtime for an existing environment. Use the local address printed at startup and stop it with npm exec --no -- wp-env stop when you’re done.
Keep this environment local. The PHP manual describes its built-in server as a development tool. Don’t expose it publicly or copy production contact records, API keys or mail credentials into it.
Give the local check a narrow job
For the contact form, start with two submissions: one missing a required field and one with a valid synthetic address. The first should show the validation message and create no record. The second should show the confirmation and create one record that you can reopen from a fresh page load. That last step matters: a confirmation in the browser only tells you what the browser displayed.
Write down what the check proved: on this plugin revision, with this WordPress and PHP version, the form accepted valid input, rejected invalid input and saved the expected record using SQLite. If the plugin sends mail, stub it or send to an isolated test sink. The local result can cover whether the plugin tried to send; it can’t show that the production mail provider accepted or delivered the message.
If the first check needs Multisite, Xdebug through wp-env, a chosen PHP version independent of your workstation, or wp-env run for CLI commands, start with the Docker runtime or another suitable environment. Those capabilities aren’t supplied by the experimental PHP runtime. Choosing a different test environment is cheaper than rewriting a useful test around the wrong tool.
Carry the same fixture to staging
On a separate staging instance, deploy the same plugin revision and repeat the two submissions. Use the actual database family and a representative PHP and web-server setup. WordPress’s hosting recommendations identify MariaDB or MySQL and HTTPS. A local pass on SQLite doesn’t exercise your production database’s SQL modes, collation or migration behavior. Likewise, a local request doesn’t traverse your staging proxy or cache.
- If the plugin changes a database table, exercise both a clean install and an upgrade from the version already deployed. Use a synthetic record created before the upgrade and confirm it remains readable afterward.
- If the form sits behind caching or a reverse proxy, open it at the staging HTTPS URL and repeat the submission as a logged-out visitor. Check the actual response and saved record, including that a rejected submission still creates none.
- If email is part of the release, use a staging-only credential and a controlled recipient. Check that the provider accepted the message and that it reached the test inbox. Keep production webhooks and real recipients disconnected.
The useful comparison is the same input against two different stacks. If both runs fail, start with the plugin revision and fixture. If only staging fails, hold the input steady and change one variable at a time: PHP version, database configuration, then the request path or provider integration. A staging-only failure is a lead, not a verdict. Capture the actual error before changing anything.
Make the handoff part of the release
A useful release note can be four lines: plugin revision, fixture used, what passed locally, and which target-stack checks passed separately. Leave anything you didn’t exercise marked unverified. If you’re also changing hosting, use the WordPress migration checklist for the separate work of testing the destination and planning cutover.
Rehearse the release on a separate WordPress stack
Use a self-managed Cube when you need control over PHP, the web server and the database for a staging rehearsal. Set up access and test integrations separately; matching production remains your responsibility.
Creation context: This AI-assisted guide uses a hypothetical plugin and documented commands. The runtime and staging workflow were not executed for this article. The cover is a generated illustration.




