Skip to content

Project Commands

Project-specific commands manage individual WordPress projects: development, builds, PHP versions, databases, repositories, and more. All project commands use the project-first grammar.

Command Format

bash
wdg <project> <command> [options]

For example: wdg my-site db export, wdg my-site wp plugin list, wdg my-site repo sync.

Lifecycle

bash
wdg my-site start        # start the project
wdg my-site stop         # stop the project
wdg my-site restart      # restart the project
wdg my-site rebuild      # rebuild the project's Docker containers
wdg my-site doctor       # run per-project diagnostics
wdg my-site doctor --fix # diagnose and auto-repair partial state

PHP Version Management

Check PHP Version

bash
wdg my-site php

Set PHP Version

bash
wdg my-site php set 8.3

Stops the container, rebuilds with the new PHP version, preserves the database and files, and restarts.

Supported versions: 8.0, 8.1, 8.2, 8.3, 8.4, 8.5.

💡 TIP

Changing PHP versions rebuilds the container but keeps all data intact, including database, uploads, and configuration.

List Available PHP Versions

bash
wdg my-site php available

Development Mode

bash
wdg my-site dev

Detects package.json in the project's theme or app directory and runs npm start with file watching. Prioritizes the theme directory when both define a package.json.

Stop the dev server with Ctrl+C.

Build

bash
wdg my-site build

Runs npm run build across the project's theme and app directories to produce production assets.

Code Quality

Lint (PHPCS)

bash
wdg my-site lint [options] [path]

Checks PHP against the WordPress Coding Standards.

Options:

  • --fix: auto-fix where possible
  • --summary: summary report
  • --severity=N: only report issues at severity N or higher

Analyze (PHPStan)

bash
wdg my-site analyze [options] [path]

Runs PHPStan static analysis.

Options:

  • --level=N: analysis level
  • --baseline: generate or use a baseline

Repository Commands

Repositories are project-scoped. The most common operations:

bash
wdg my-site repo add <url> [branch] [--type=...]   # clone + mount a repo
wdg my-site repo list                              # list project repos
wdg my-site repo status                            # git status for all repos
wdg my-site repo sync [repo-name]                  # git pull (all or one)
wdg my-site repo init <repo-name>                  # init a directory as a repo
wdg my-site repo hooks <repo-name>                 # (re)install git hooks
wdg my-site repo attach <path> [--type=...]        # mount an external repo
wdg my-site repo remove <repo-name>                # remove a repo

See Repository Management for the full reference, including mount types and flags.

Sidecar Services

Sidecar services let you run extra Docker containers (search engines, caches, mail catchers) next to a project's WordPress container so you can recreate a hosting integration locally instead of mocking it.

Sidecars are per-project. Each one joins the shared wdg-network and is reachable from the project's WordPress container by its container name, wdg-<project>-<service> (for example wdg-my-site-typesense). WordPress always talks to a sidecar over the Docker network using the service's container port, never a host port. The connection details (host, port, credentials) are injected into the WordPress container as environment variables automatically; project code consumes them.

bash
# Add from the curated catalog
wdg my-site service add typesense       # search: typesense, elasticsearch, opensearch, meilisearch
wdg my-site service add wpvip-search    # WP VIP Enterprise Search (Elasticsearch 7.10.x + ElasticPress/VIP env)
wdg my-site service add redis           # cache/queue: redis, memcached
wdg my-site service add mailpit         # mail catcher: mailpit, mailhog

# Add any image (escape hatch)
wdg my-site service add --image=clickhouse/clickhouse-server --port=8123 --name=clickhouse

# Inspect / remove
wdg my-site service list                       # name, catalog, image, browser URL, persistence
wdg my-site service remove typesense           # keeps the data volume by default
wdg my-site service remove typesense --purge   # also deletes the data volume

Curated Catalog

wdg <project> service add <name> accepts any of the catalog keys below. Each preset pins a known-good image, picks sensible defaults, and injects the connection environment variables WordPress needs. The container port column is the in-container port: for UI services the browser hits it (mapped to an auto-allocated host port), and for headless services it is the wire port WordPress connects to over the Docker network. The injected env column lists the exact variables added to the WordPress container. The @HOST token is replaced at add-time with the sidecar's container DNS name.

ServiceWhat it isContainer portBrowser UIInjected WordPress env
typesenseTypesense 0.25.2 typo-tolerant search engine8108YesTYPESENSE_HOST, TYPESENSE_PORT (8108), TYPESENSE_API_KEY (wdg-local-dev)
elasticsearchElasticsearch 8.13.0 (single-node, security disabled)9200YesELASTICSEARCH_HOST, ELASTICSEARCH_PORT (9200)
opensearchOpenSearch 2.13.0 (single-node, security plugin disabled)9200YesOPENSEARCH_HOST, OPENSEARCH_PORT (9200)
meilisearchMeilisearch v1.7 (development mode)7700YesMEILISEARCH_HOST, MEILISEARCH_PORT (7700), MEILISEARCH_KEY (wdg-local-dev)
wpvip-searchWP VIP Enterprise Search (Elasticsearch 7.10.2)9200YesEP_HOST (http://@HOST:9200), VIP_ELASTICSEARCH_ENDPOINTS (http://@HOST:9200), VIP_ELASTICSEARCH_USERNAME (empty), VIP_ELASTICSEARCH_PASSWORD (empty)

Cache / Queue

ServiceWhat it isContainer portBrowser UIInjected WordPress env
redisRedis 7-alpine6379No (headless)REDIS_HOST, REDIS_PORT (6379)
memcachedMemcached 1.6-alpine11211No (headless)MEMCACHED_HOST, MEMCACHED_PORT (11211)

Mail catcher

Both mail catchers expose their web inbox on container port 8025 (mapped to a host port) and accept SMTP on 1025. The injected env points WordPress at the SMTP endpoint, not the inbox UI.

ServiceWhat it isContainer port (UI)Browser UIInjected WordPress env
mailpitMailpit (latest) mail catcher8025YesWORDPRESS_SMTP_HOST, WORDPRESS_SMTP_PORT (1025)
mailhogMailHog v1.0.1 mail catcher8025YesWORDPRESS_SMTP_HOST, WORDPRESS_SMTP_PORT (1025)

WP VIP Enterprise Search

wpvip-search runs Elasticsearch 7.10.2 to mirror WP VIP's Enterprise Search locally. VIP pins the 7.10.x line, so this preset matches that version rather than the generic elasticsearch 8.x preset.

Unlike the other search presets, it requires project-side wiring. The sidecar provides only the engine and the connection env (EP_HOST, VIP_ELASTICSEARCH_ENDPOINTS, and empty VIP_ELASTICSEARCH_USERNAME / VIP_ELASTICSEARCH_PASSWORD). The project still needs:

  • ElasticPress / vip-go-mu-plugins installed in its codebase, and
  • a wp-config (or mu-plugin) bridge that maps the injected env vars to the matching PHP constants.

It does not install the plugins or write the bridge for you.

The --image Escape Hatch

When no catalog preset fits, add any Docker image directly:

bash
# Headless ClickHouse, no browser UI
wdg my-site service add --image=clickhouse/clickhouse-server --port=8123 --name=clickhouse --no-ui

# A custom service with a persistent volume and WordPress connection env
wdg my-site service add \
  --image=foo/bar:1.2 \
  --port=9000 \
  --name=bar \
  --volume \
  --wp-env=FOO_HOST=@HOST,FOO_PORT=9000
FlagRequiredBehavior
--port=<container-port>YesThe service's primary in-container port.
--name=NAMENoSidecar name (becomes the container suffix wdg-<project>-<name>). Defaults to the image's last path segment with any tag stripped (e.g. clickhouse/clickhouse-server:latest becomes clickhouse). Must be lowercase letters, digits, and hyphens.
--no-uiNoTreat the service as headless, skipping host-port allocation. Omit it and the escape-hatch service is treated as having a UI on --port, so it gets a host port.
--volumeNoAttach a persistent named volume mounted at /data in the container.
--wp-env=K=V,K=VNoComma-separated env vars injected into WordPress. The literal token @HOST expands to the sidecar's container DNS name (wdg-<project>-<name>).

How It Works

  • Connection env vars are connectivity hints. The platform injects host/port/credential variables into the WordPress container, but project code (theme, plugin, or wp-config) is what actually consumes them. Adding a sidecar wires up reachability; it does not configure WordPress to use the service.
  • Host ports for browser UIs. A service with a UI gets a host port auto-allocated from SIDECAR_PORT_RANGE_START..SIDECAR_PORT_RANGE_END (default 7000 to 7999). Allocation is de-conflicted across all projects' sidecars, so two projects never collide on the same host port. Headless services (redis, memcached) get no host port at all.
  • State file. A project's sidecars live in projects/<name>/docker-compose.sidecars.yml. The file is regenerated in full on every add/remove from a canonical # wdg-sidecars-v1: JSON line it carries at the top. That line, not the rendered YAML, is the source of truth. Do not hand-edit this file; changes are overwritten.
  • Separate from the override file. The sidecars file is distinct from docker-compose.override.yml, which the repo-mount system owns and regenerates from repositories.json. get_docker_compose_cmd merges the base compose file, the override file, and the sidecars file together, so sidecar containers and repo volume mounts coexist.

Lifecycle

Sidecars are bound to their project's lifecycle:

bash
wdg my-site start     # starts WordPress and its sidecars
wdg my-site stop      # stops WordPress and its sidecars
wdg my-site restart   # restarts both

They appear under the project in wdg status and wdg list. Note that wdg start with no project (core services only) does not touch any project's sidecars.

After an add or remove, if the project is already running the change is applied immediately (the stack is brought up with the merged compose config). If the project is stopped, the change applies on the next wdg my-site start.

Inspect and Remove

bash
wdg my-site service list

Lists each sidecar with its name, catalog key (or custom for --image services), image, browser URL(s) (or (internal only) for headless services), and whether its data persists.

bash
wdg my-site service remove typesense           # keeps the data volume
wdg my-site service remove typesense --purge   # also deletes the data volume

Removing a sidecar rewrites the sidecars file (or deletes it when the last sidecar is removed), force-removes the container, and re-applies the stack so WordPress drops the removed service's env on its next recreate. The data volume is kept by default. Pass --purge to delete it; on an interactive terminal without --purge, you are prompted before the volume is deleted.

Troubleshooting

  • Sidecar didn't start with the project. The project's start/recreate must go through get_docker_compose_cmd so the sidecars file is merged in. Re-run wdg my-site start (or wdg my-site restart).
  • UI port conflict / "No free host port". Host ports are allocated from SIDECAR_PORT_RANGE_START..END (default 7000 to 7999). If the range is exhausted, widen it in .env. Keep it disjoint from the 6xxx platform ports (nginx, MySQL, Qdrant, MCP server) to avoid collisions.
  • Sidecar disappeared after repo add or doctor --fix. Sidecar state must live in docker-compose.sidecars.yml. The repo-mount system owns and regenerates docker-compose.override.yml, so anything misplaced there is lost. Re-add with wdg my-site service add.

Database Commands

Export Database

bash
wdg my-site db export [filename]

Examples:

bash
# Auto-generated, timestamped filename
wdg my-site db export

# Custom filename
wdg my-site db export backup-$(date +%Y%m%d).sql

Import Database

bash
wdg my-site db import <sql-file>
bash
wdg my-site db import production-db.sql

⚠️ WARNING

Importing replaces existing data in the project database.

Reset Database

bash
wdg my-site db reset

Resets the database to a fresh WordPress installation.

Search & Replace

bash
wdg my-site db search-replace <search> <replace>

Examples:

bash
wdg my-site db search-replace "oldsite.com" "newsite.com"
wdg my-site db search-replace "https://my-site.localhost:6443" "https://production.com"

Sync from Hosting Platforms

bash
wdg my-site sync <pantheon|wpvip|wpengine|kinsta> [options]

Pulls the database (and optionally media) from a hosting platform.

Examples:

bash
wdg my-site sync pantheon --site=mysite --env=live --media
wdg my-site sync wpvip --app=12345 --env=production --media
wdg my-site sync wpengine --install=coenetorg --prod-url=coenet.org --media --plugins
wdg my-site sync kinsta --site=mysite --env=live --media

See Connecting hosting platforms for WP Engine and Kinsta setup.

WordPress CLI

bash
wdg my-site wp <wp-cli-command>

Runs WP-CLI inside the project container (auto-starting the project if it is stopped).

Examples:

bash
wdg my-site wp plugin list
wdg my-site wp plugin activate custom-plugin
wdg my-site wp theme activate my-site-theme
wdg my-site wp user create john john@example.com --role=administrator
wdg my-site wp cache flush
wdg my-site wp cron event run --all
wdg my-site wp db query "SELECT * FROM wp_users"

Indexing

bash
wdg my-site index

Indexes the project's repositories into its project_<name> vector collection. See All CLI Commands for wdg index --platform and Wikit indexing.

Logs

bash
wdg my-site logs [type] [options]

View project logs by type: debug, php, nginx, container, all.

Options:

  • --follow / -f: follow live
  • --tail=N: last N lines
  • --since=<time>: entries since a time
  • --grep=<pattern>: filter by pattern

Examples:

bash
wdg my-site logs all                 # triage all log sources
wdg my-site logs debug               # WordPress debug.log
wdg my-site logs debug -f            # follow debug.log live
wdg my-site logs nginx --grep 500    # nginx 500 errors

Project Manifest

The manifest records project metadata (client, Jira project, hosting platform, etc.).

bash
wdg my-site manifest init     # initialize interactively
wdg my-site manifest show     # display the manifest
wdg my-site manifest get <path>          # read a field, e.g. .hosting.site
wdg my-site manifest set <path> <value>  # set a field
wdg my-site manifest edit     # open in $EDITOR

manifest init accepts --client=NAME, --jira=KEY, --hosting-platform=PLATFORM, and --hosting-site=SITE.

Memory Store

Per-project memory store used for ticket tracking and related state.

bash
wdg my-site memory init      # create the store
wdg my-site memory status    # show store status
wdg my-site memory reset     # reset the store
wdg my-site memory export    # export the store
wdg my-site memory import    # import into the store

Skills & AI Config

bash
wdg my-site skills sync [skill] [--force]   # sync skill templates into this project
wdg my-site ai-config sync [--all] [--force] # sync AI assistant config + agents

The org-wide forms (wdg skills, wdg ai-config) are documented in All CLI Commands.

Playwright

bash
wdg my-site playwright        # project-scoped Playwright browser automation

Common Workflows

Daily Development

bash
wdg my-site start
wdg my-site repo sync
wdg my-site dev
# ... make changes, git commit (triggers auto-indexing) ...
wdg my-site stop

Switching PHP Versions

bash
wdg my-site php           # check current version
wdg my-site php set 8.3   # change (container rebuilds automatically)
wdg my-site php           # verify

Production Build Process

bash
wdg my-site build
wdg my-site db export production.sql
wdg my-site db search-replace "my-site.localhost:6443" "production-domain.com"

See Also: