wp-env vs Docker Compose: Which Approach Better Handles Database State Sharing in Repeatable WordPress Environments?
0 reputation · 28 Sept 2025, 16:38 UTC
0 reputation · 28 Sept 2025, 16:38 UTC
Teams that spin up local WordPress instances with wp-env or a hand‑written Docker Compose stack face the same challenge: how to make the content, options, and user accounts reproducible across machines and over time. The core WordPress, plugins, and theme code can be version‑controlled with a .wp-env.json or a Compose YAML, but the MySQL volume that holds the database state is not automatically shared.
Two common strategies exist. The first is to commit a sanitized SQL dump to the repository and restore it each time the environment starts. The second is to run a seed script—typically a series of WP‑CLI commands—after the containers are up. Both approaches have trade‑offs around data privacy, maintenance burden, and consistency with evolving content models.
When choosing between wp-env and Docker Compose, the decision is further complicated by how each tool handles core, plugin, and theme version pinning, the need for Node.js in wp-env, and the ability to hook lifecycle scripts for post‑start seeding.
Given these constraints, the unresolved decision is: how should teams share reproducible database state while keeping the environment truly repeatable?
30775 reputation · 28 Sept 2025, 22:52 UTC
Neither wp-env nor Docker Compose shares database state by itself—both keep MySQL data in a local Docker volume that never leaves the machine. The tool choice matters less than the seeding strategy, and for most teams a WP-CLI seed script beats a committed SQL dump for repeatability. wp-env makes that strategy easier to wire up; Compose gives you more explicit control at the cost of maintaining the plumbing yourself.
A static SQL dump freezes a snapshot of the schema. When you bump WordPress core or a plugin version, the dump can become stale or incompatible—upgrade routines expect to run against a live database, not a restored artifact from an older release. A seed script (a sequence of wp post create, wp option update, wp user create, etc.) runs against the current schema every time, so it stays consistent as versions evolve. It also avoids the privacy risk of a dump: there are no real user emails, hashed credentials, or API keys to accidentally commit, because the data is generated, not exported.
The dump approach still has a place—when you need a large, realistic content set that is impractical to script—but it must be aggressively sanitized and treated as a periodic regeneration task, not a one-time export.
With wp-env, you add a lifecycle script (for example via "lifecycleScripts" or a post-start command in your package scripts) that runs WP-CLI inside the container after wp-env start. One caveat: these hooks only fire through wp-env commands—if someone starts the underlying containers with docker compose up directly, the seed never runs.
With hand-written Compose, you replicate this with an entrypoint or a one-shot "seeder" service that waits on a MySQL healthcheck, then runs the same WP-CLI commands. More YAML to maintain, but no Node.js dependency and full control over image tags and volumes.
Pinning core, plugins, and themes in .wp-env.json is conceptually equivalent to locking image tags in Compose, with one gap: wp-env abstracts the base images away, so underlying image drift is still possible. Compose lets you pin every tag explicitly. For long-term repeatability, pin what each tool exposes and treat the seed script as the source of truth for content.
wp post create guarded by existence checks, or wp option update which is naturally idempotent).wp-env start (or docker compose up) on a second machine and query a known option or post via WP-CLI; confirm identical results.Note: lifecycle hook names and wp-env configuration fields have evolved across releases—verify the exact hook syntax against the version of @wordpress/env you have installed before committing to this setup.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.