Skip to content

Missing features and issues when trying to work with @wordpress/env (wp-env) #137

Description

@afragen

I would love to use Apple/container with WordPress's docker environment. I ran into several issues and will apologize in advance for the AI details below.

Fundamentally I believe the primary issue is missing exec / port / ps / logs subcommands. What follows is the full AI testing report.

Issue: container-compose cannot run @wordpress/env (wp-env) multi-service stacks

Summary

Container-Compose 1.1.0 (Homebrew homebrew/core; upstream
github.com/Mcrich23/Container-Compose) cannot bring up a
@wordpress/env ("wp-env") environment. wp-env emits a standard
docker-compose.yml (~7 services: 2 MariaDB, 2 WordPress, 2 CLI,
2 phpMyAdmin) and drives it through the docker / docker compose
CLI. Three gaps block it:

  1. Missing exec / port / ps / logs subcommands — wp-env
    calls docker compose exec (for wp-env run) and docker compose port
    (to read the mapped MySQL host port). container-compose exposes only
    up / down / build / version.
  2. Host-port collision when two services bind the same host port
    booting the dev + tests MariaDB together with identical port mappings
    deterministically fails with
    bind(descriptor:ptr:bytes:): Address already in use (errno: 48).
  3. No pull subcommand — wp-env calls docker compose pull;
    container-compose auto-pulls inside up, so pull is a no-op
    workaround, but the missing surface is still a parity gap.

Note: this report was produced while evaluating container-compose as a
replacement for Podman / Docker Desktop. The environment was reverted to
Podman afterward. Findings below are reproducible on a clean
Apple container 1.1.0 + container-compose 1.1.0 install.


Environment (exact)

Component Version
macOS 26.5.2 (Tahoe)
Apple container 1.1.0 (Homebrew homebrew/core, release unspecified)
container-compose 1.1.0 (Homebrew homebrew/core)
@wordpress/env ^10.0
Test driver composer wp-env-startwp-env start --xdebug=coverage

The docker CLI is shimmed to container-compose (a thin
bin/docker wrapper translating docker compose …container-compose …).
No Docker Desktop, no Podman involved in the Apple path.


Issue 1 (blocker): Missing exec / port / ps / logs

What wp-env needs

wp-env's runtime
(@wordpress/env/lib/runtime/docker/index.js) shells out to:

  • docker compose up -d mysql then
    docker compose up -d wordpress tests-wordpress cli tests-cli
  • docker compose port <svc> <port> — to read the mapped MySQL port
    (e.g. docker compose port mysql 33060.0.0.0:46239)
  • docker compose exec <svc> … — for wp-env run tests-cli …
    (the composer test flow)
  • docker compose down / ps / logs

What container-compose provides

$ container-compose --help
SUBCOMMANDS:
  up        Start containers with compose
  down      Stop containers with compose
  build     Build images from a compose file without starting containers
  version   Display the version information

There is no exec, port, ps, or logs. Consequently, even
if up succeeds, these wp-env flows are impossible:

  • wp-env run tests-cli wp --version → needs compose execno such command
  • Port discovery (_getPublicDockerPort) → needs compose portno such command
  • Any composer test invocation → routes through compose execfails

Expected behavior

For wp-env compatibility, container-compose needs at minimum
exec and port subcommands mirroring docker compose exec /
docker compose port semantics (including -f <file> handling and the
<service> <container_port> argument shape wp-env emits).


Issue 2 (blocker for the tests stack): Host-port collision

Verbatim reproduction

Clean slate, then boot both database services together (this is what
wp-env does internally — it brings up mysql first, then the full
wordpress tests-wordpress cli tests-cli set which pulls in tests-mysql
as a dependency):

container-compose -f ~/.wp-env/<hash>/docker-compose.yml \
  up -d mysql tests-mysql

With both services mapped to the same host port (e.g.
WP_ENV_MYSQL_PORT=17888 WP_ENV_TESTS_MYSQL_PORT=17888):

Note: Service 'mysql' is not explicitly connected to any networks.
      It will likely use the default bridge network.
Starting service: mysql
Starting mysql
----------------------------------------

mysql: [6/6] Starting container [0s]
mysql: [6/6] Starting container [0s]
mysql: git-updater-mysql
Note: Service 'tests-mysql' is not explicitly connected to any networks.
      It will likely use the default bridge network.
Starting service: tests-mysql
Starting tests-mysql
----------------------------------------

  tests-mysql: [6/6] Starting container [0s]
Error: failed to bootstrap container
        (cause: "internalError: "failed to bootstrap container git-updater-tests-mysql
                 (cause: "unknown: "bind(descriptor:ptr:bytes:): Address already in use
                 (errno: 48)"")")

(errno: 48 = EADDRINUSE.)

Root-cause distinction (important)

The compose file maps both DB services to distinct host ports via
env vars, with distinct internal 3306:

mysql:
  ports:
    - '${WP_ENV_MYSQL_PORT:-}:3306'
tests-mysql:
  ports:
    - '${WP_ENV_TESTS_MYSQL_PORT:-}:3306'

Two separate failure modes were observed and should not be conflated:

  • (a) Same port → 100% collision (deterministic). When both
    WP_ENV_MYSQL_PORT and WP_ENV_TESTS_MYSQL_PORT resolve to the
    same value, tests-mysql fails to boot every time with the
    errno: 48 error above. Reproduced 5/5 attempts.
  • (b) Distinct ports → reliable success. With
    WP_ENV_MYSQL_PORT=17701 WP_ENV_TESTS_MYSQL_PORT=17702,
    both containers booted cleanly across 5/5 attempts.
  • (c) Apple container service crash (separate issue). The stock
    homebrew/core container formula does not register a launchd
    service the way the stephenlclarke/container fork does. The API
    server intermittently dies mid-run with
    XPC connection error: Connection invalid, after which
    container ls itself errors and no containers start. This was a
    separate source of "nothing booted" that initially looked like a
    port collision. Restarting with container system start recovers it.

Expected behavior

Each service's ${VAR:-} port mapping should resolve to a distinct,
free host port (or honor the explicit WP_ENV_TESTS_MYSQL_PORT
value) without colliding with the sibling DB service — matching
Docker Compose / Podman behavior, where the distinct-port case boots
reliably every time.


Issue 3 (minor parity gap): No pull subcommand

wp-env's start flow calls docker compose pull (via the
docker-compose npm package's pullAll). container-compose has
no pull subcommand:

$ container-compose pull
Error: Unexpected argument 'pull'
Usage: container-compose [--file <file>] [--profile <profile>] ...] <subcommand>

In practice up auto-pulls images, so mapping pull → no-op works
as a shim around wp-env — but the missing surface is still a parity
gap versus docker compose.


Additional observation: port-default mismatch in wp-env's generated file

wp-env's generated docker-compose.yml uses these port defaults:

wordpress:
  ports:
    - '${WP_ENV_PORT:-8888}:80'        # NOTE: 8888, not 7778
tests-wordpress:
  ports:
    - '${WP_ENV_TESTS_PORT:-8889}:80'     # NOTE: 8889, not 7779

The npm script sets WP_ENV_PORT=7778 WP_ENV_TESTS_PORT=7779,
but the compose file's own :- fallback is 8888 / 8889.
container-compose interpolates ${VAR:-default} from the shell
environment at up time
. When the var is absent it falls back to
8888 (which happened to be free, so up succeeded). This is a
wp-env-side quirk, but it interacts with Issue 2: if any two
port variables ever resolve to the same value (explicit or via
fallback), container-compose has no collision-avoidance and fails
hard with errno: 48 rather than reallocating like Docker Compose.


What does work (for completeness)

Against stock Apple container 1.1.0 via container-compose 1.1.0:

  • container-compose -f <file> up -d mysql — dev DB starts
  • container-compose -f <file> down — teardown
  • ✅ Named-volume creation is idempotent (no "already exists" error
    on re-up), unlike the stephenlclarke/container-compose fork
    which fails here
  • ✅ Project name defaults to the compose-file directory name
    (not the wp-env hash) — fine

So container-compose is closer to wp-env-usable than the
stephenlclarke fork was, but the three gaps above still block a full
@wordpress/env run (and the container service-crash in (c) is a
separate environment fragility).


Addendum: can the port collision be worked around?

Partially, and it does NOT unblock wp-env.

The host-port collision (Issue 2a) is avoidable: when the two MariaDB
services map to distinct host ports it boots reliably. Verified
5/5 attempts with WP_ENV_MYSQL_PORT=17901 WP_ENV_TESTS_MYSQL_PORT=17902:

--- attempt 1: mysql=17901 tests-mysql=17902 ---
OK both booted
... (attempts 2-5 identical: OK both booted)

So forcing distinct ports removes the errno: 48 collision
deterministically. But wp-env still cannot complete, because the
missing exec / port subcommands (Issue 1) are the real
blocker, independent of ports:

  • wp-env start calls docker compose port mysql 3306 to discover
    the mapped MySQL host port and print "MySQL is listening on port X".
    container-compose has no port subcommand → this step fails.
  • wp-env run / composer test route through
    docker compose exec <svc> …no exec subcommand → impossible.

Conclusion: fixing the port mapping (Issue 2a) is a nice-to-have,
but wp-env only becomes usable once Issue 1 (exec + port) is
resolved upstream. Until then, Podman remains the only functional
backend, and the port-collision detail is secondary context for the
maintainer rather than a standalone fix.


Suggested minimal fix set for wp-env support

  1. Add exec and port subcommands (highest priority — without
    these, wp-env run / port discovery are impossible).
  2. Fix same-port collision in up (Issue 2a) — either detect /
    reject duplicate host-port mappings up front, or allocate a free
    port like Docker Compose when the mapping conflicts.
  3. Add pull (or accept + ignore pull for Docker-Compose
    compatibility).
  4. (Nice-to-have) ps / logs for parity.

Happy to test a build / --HEAD install if any of these land.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions