agentsclimarketplace

Wp docker compose stack

Skill Lonsdale201/wp-agent-skills/dev-tooling/wp-docker-compose-stack

A community-maintained collection of agent skills for WordPress plugin and theme development.

Install
npx -y skills add Lonsdale201/wp-agent-skills --skill wp-docker-compose-stack

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 21 stars21 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Build a custom docker-compose development stack for WordPress when wp-env's abstraction runs out — Redis object cache, Mailpit SMTP capture, custom PHP extensions (phpredis, Xdebug), php.ini overrides. Covers the official wordpress image's runtime-env config model (wp-config.php reads WORDPRESS_DB_* / WORDPRESS_CONFIG_EXTRA via getenv at request time, so every container sharing the volume — wp-cli included — needs the same environment, best shared via a YAML anchor), the mariadb healthcheck + service_healthy dependency, a wp-cli service as user 33:33, wiring the redis-cache plugin drop-in with WP_REDIS_HOST, why wp_mail silently fails (invalid wordpress@localhost From) and the phpmailer_init + Mailpit fix, pecl Dockerfiles for phpredis/Xdebug, and pinning image tags because wordpress:latest lags WP releases. Use when composing docker-compose.yml for WP, adding Redis/mail capture to local dev, or debugging a WP container where config env vars seem ignored.

The file declares its own license as GPLv2-or-later. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

11.6 KB, ~2.6k tokens by cl100k_base, as published. Nobody here has run it

A custom docker-compose stack for WordPress

Hand-write a compose stack when you need services the official tooling doesn't model: Redis object cache, SMTP capture, custom PHP extensions, production-like topology. Default to wp-env first (see wp-env-local-dev) — it covers plain plugin/block development with less to maintain. This skill is the escape hatch, grounded against a fully booted and verified stack (WordPress + MariaDB + Redis + Mailpit + wp-cli, drop-in connected, mail captured). The complete tested files are bundled under examples/.

When to use this skill

  • Writing or reviewing a docker-compose.yml for WordPress development.
  • Adding Redis (object cache), Mailpit (mail capture), Xdebug, or php.ini overrides to a WP container setup.
  • Debugging "my WORDPRESS_CONFIG_EXTRA / WP_REDIS_HOST is ignored" or "wp-cli sees different config than the site".
  • wp_mail silently fails inside a container.
  • A team needs a production-like local topology that wp-env can't express.

The config model: env is read at request time

This is the load-bearing fact of the official wordpress image, and the source of most broken stacks. The generated wp-config.php does not bake values in — it calls getenv_docker() on every request, and WORDPRESS_CONFIG_EXTRA is eval'd from the environment at runtime.

Consequence (verified by hitting it): a wp-cli container sharing the same volume gets none of that config unless it has the same environment variables. Our wp redis enable failed with Connection refused [tcp://127.0.0.1:6379] because only the wordpress service had WORDPRESS_CONFIG_EXTRA with WP_REDIS_HOST — the wp-cli service silently fell back to defaults. Share the block with a YAML anchor:

x-wp-environment: &wp-environment
  WORDPRESS_DB_HOST: db
  WORDPRESS_DB_USER: wordpress
  WORDPRESS_DB_PASSWORD: wordpress
  WORDPRESS_DB_NAME: wordpress
  WORDPRESS_CONFIG_EXTRA: |
    define( 'WP_REDIS_HOST', 'redis' );
    if ( ! defined( 'WP_DEBUG' ) ) { define( 'WP_DEBUG', true ); }
    define( 'WP_DEBUG_LOG', true );

services:
  wordpress:
    environment: *wp-environment
  wpcli:
    environment: *wp-environment

Two refinements, both verified:

  • Guard WP_DEBUG with if ( ! defined(...) ) — wp-cli defines it first and an unguarded define in CONFIG_EXTRA throws "Constant WP_DEBUG already defined" warnings on every cli call. (Alternative: the image's own WORDPRESS_DEBUG: 1 env var toggles WP_DEBUG without touching CONFIG_EXTRA.)
  • Other image env vars: WORDPRESS_TABLE_PREFIX, the WORDPRESS_AUTH_KEYWORDPRESS_NONCE_SALT salts (auto-generated if omitted), and a _FILE suffix convention on all of them for Docker secrets.

Baseline stack

Abbreviated (full tested file: examples/docker-compose.yml):

services:
  db:
    image: mariadb:lts
    environment:
      MARIADB_DATABASE: wordpress
      MARIADB_USER: wordpress
      MARIADB_PASSWORD: wordpress
      MARIADB_ROOT_PASSWORD: root
    volumes: [ db_data:/var/lib/mysql ]
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 5s
      timeout: 5s
      retries: 12

  wordpress:
    image: wordpress:latest        # pin in real projects — see below
    depends_on:
      db:
        condition: service_healthy
    ports: [ "8080:80" ]
    environment: *wp-environment
    volumes:
      - wp_data:/var/www/html
      - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
  • The healthcheck is not optional. WordPress crashes into a restart loop if it races a still-initializing MariaDB; condition: service_healthy with mariadb's bundled healthcheck.sh (verified) removes the race.
  • Pin the image. wordpress:latest lags WordPress releases — at test time it shipped WP 6.8.3 / PHP 8.3 while the current release was 7.0.1 (which wp-env did install). Use an explicit tag like wordpress:6.8-php8.3-apache, and update WP itself via wp core update in the volume when needed.
  • php.ini overrides are a one-line mount into /usr/local/etc/php/conf.d/ — verified upload_max_filesize/memory_limit took effect. See examples/uploads.ini.
  • For plugin development, add a bind mount: ./:/var/www/html/wp-content/plugins/my-plugin (on top of the named volume).

wp-cli as a service

  wpcli:
    image: wordpress:cli
    user: "33:33"
    profiles: ["cli"]              # doesn't start with `up`, only via `run`
    depends_on:
      db: { condition: service_healthy }
    environment: *wp-environment
    volumes: [ wp_data:/var/www/html ]
docker compose run --rm wpcli wp core install \
  --url=http://localhost:8080 --title="Dev" \
  --admin_user=admin --admin_password=admin \
  [email protected] --skip-email
  • user: "33:33" matters: the Alpine-based cli image defaults to uid 82, the Debian Apache image serves as uid 33 (www-data). Verified: with 33:33, files wp-cli writes (plugin installs, the Redis drop-in) come out owned by www-data and stay editable by the web container.
  • The Failed to create directory '/.wp-cli/cache/' warning is benign (uid 33 has no writable HOME); silence it with HOME: /tmp in the environment if it bothers you.
  • Wait for first boot before installing: the wordpress container generates wp-config.php on first run — until then wp-cli has nothing to load.

Redis object cache (verified end-to-end)

  redis:
    image: redis:alpine

With WP_REDIS_HOST pointing at the service name (in the shared anchor above):

docker compose run --rm wpcli wp plugin install redis-cache --activate
docker compose run --rm wpcli wp redis enable
docker compose run --rm wpcli wp redis status   # → Status: Connected, Drop-in: Valid, Ping: PONG

The official image ships no phpredis extension — verified: the drop-in falls back to the plugin's bundled Predis client, which works fine for dev. For phpredis (production parity), a two-line Dockerfile (verified build): FROM wordpress:latest + RUN pecl install redis && docker-php-ext-enable redis. For what the object cache changes about wp_cache_* semantics, see wp-redis-object-cache.

Mail capture with Mailpit (and why wp_mail fails first)

  mailpit:
    image: axllent/mailpit
    ports: [ "8025:8025" ]         # web UI + API; SMTP is 1025 inside the network

Two things must both be fixed — verified by hitting each failure separately:

  1. WordPress doesn't speak SMTP by default (and the container has no sendmail): route PHPMailer to Mailpit.
  2. The default From address is invalid in Docker. With a localhost site URL, WP builds From: wordpress@localhost, which PHPMailer rejects (Invalid address) — so wp_mail() returns false before any SMTP attempt, with no output unless you listen to wp_mail_failed.

Drop this as an mu-plugin (wp-content/mu-plugins/mailpit.php):

<?php
add_action( 'phpmailer_init', function ( $phpmailer ) {
	$phpmailer->isSMTP();
	$phpmailer->Host = 'mailpit';
	$phpmailer->Port = 1025;
} );
add_filter( 'wp_mail_from', fn() => '[email protected]' );

Verified: with both in place the message lands in Mailpit (UI at http://localhost:8025, scriptable via GET /api/v1/messages). When debugging, hook wp_mail_failed and print the WP_Error — it names the real cause.

Xdebug

The official image has no Xdebug; add it with a Dockerfile (verified build, Xdebug 3.5.3 — full file: examples/Dockerfile.xdebug):

FROM wordpress:latest
RUN pecl install xdebug && docker-php-ext-enable xdebug

Configure via a conf.d ini: xdebug.mode=debug, xdebug.start_with_request=trigger, xdebug.client_host=host.docker.internal, xdebug.client_port=9003. host.docker.internal reaches the IDE on Docker Desktop; on native Linux add it via extra_hosts: ["host.docker.internal:host-gateway"].

Critical rules

  • Every container that loads WordPress needs the same WORDPRESS_* environment — the image resolves config from env at request time, not at install time. Use a YAML anchor; a config that "works on the site but not in wp-cli" is this bug.
  • Healthcheck the DB and gate WordPress on service_healthydepends_on alone only orders startup, it doesn't wait for MySQL to accept connections.
  • Pin image tags. wordpress:latest lags WP releases and silently changes PHP major versions under you.
  • Run wp-cli as user: "33:33" so written files stay owned by www-data; alpine-cli's default uid 82 leaves root-unfriendly ownership mismatches.
  • wp_mail in a container needs a valid From address AND an SMTP route — fix both (wp_mail_from + phpmailer_init), and debug via wp_mail_failed, never by staring at a silent false.
  • Guard constants in CONFIG_EXTRA (if ( ! defined(...) )) — wp-cli pre-defines some (WP_DEBUG) and unguarded defines warn on every invocation.
  • Named volumes for /var/www/html and the DBdocker compose down keeps them, down -v is the deliberate wipe.

Cross-references

  • Run wp-env-local-dev first — if the project needs no custom services, the official tool is less to maintain.
  • Run wp-redis-object-cache for correct wp_cache_* usage and drop-in lifecycle once Redis is wired.
  • Run wp-phpunit-test-setup for the test harness; its wp-env route already includes the WP test suite, no extra compose service needed.

What this skill does NOT cover

  • Production hosting, TLS, scaling, or hardening — this is a development stack.
  • wp-env itself (see wp-env-local-dev) and WordPress Playground.
  • Node build tooling (Tailwind, @wordpress/scripts): run watchers on the host — file-watching through container mounts is slow and gains nothing locally; containerize builds only in CI.

References

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.