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
wdg <project> <command> [options]For example: wdg my-site db export, wdg my-site wp plugin list, wdg my-site repo sync.
Lifecycle
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 statePHP Version Management
Check PHP Version
wdg my-site phpSet PHP Version
wdg my-site php set 8.3Stops 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
wdg my-site php availableDevelopment Mode
wdg my-site devDetects 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
wdg my-site buildRuns npm run build across the project's theme and app directories to produce production assets.
Code Quality
Lint (PHPCS)
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 severityNor higher
Analyze (PHPStan)
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:
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 repoSee 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.
# 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 volumeCurated 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.
Search
| Service | What it is | Container port | Browser UI | Injected WordPress env |
|---|---|---|---|---|
typesense | Typesense 0.25.2 typo-tolerant search engine | 8108 | Yes | TYPESENSE_HOST, TYPESENSE_PORT (8108), TYPESENSE_API_KEY (wdg-local-dev) |
elasticsearch | Elasticsearch 8.13.0 (single-node, security disabled) | 9200 | Yes | ELASTICSEARCH_HOST, ELASTICSEARCH_PORT (9200) |
opensearch | OpenSearch 2.13.0 (single-node, security plugin disabled) | 9200 | Yes | OPENSEARCH_HOST, OPENSEARCH_PORT (9200) |
meilisearch | Meilisearch v1.7 (development mode) | 7700 | Yes | MEILISEARCH_HOST, MEILISEARCH_PORT (7700), MEILISEARCH_KEY (wdg-local-dev) |
wpvip-search | WP VIP Enterprise Search (Elasticsearch 7.10.2) | 9200 | Yes | EP_HOST (http://@HOST:9200), VIP_ELASTICSEARCH_ENDPOINTS (http://@HOST:9200), VIP_ELASTICSEARCH_USERNAME (empty), VIP_ELASTICSEARCH_PASSWORD (empty) |
Cache / Queue
| Service | What it is | Container port | Browser UI | Injected WordPress env |
|---|---|---|---|---|
redis | Redis 7-alpine | 6379 | No (headless) | REDIS_HOST, REDIS_PORT (6379) |
memcached | Memcached 1.6-alpine | 11211 | No (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.
| Service | What it is | Container port (UI) | Browser UI | Injected WordPress env |
|---|---|---|---|---|
mailpit | Mailpit (latest) mail catcher | 8025 | Yes | WORDPRESS_SMTP_HOST, WORDPRESS_SMTP_PORT (1025) |
mailhog | MailHog v1.0.1 mail catcher | 8025 | Yes | WORDPRESS_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-pluginsinstalled 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:
# 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| Flag | Required | Behavior |
|---|---|---|
--port=<container-port> | Yes | The service's primary in-container port. |
--name=NAME | No | Sidecar 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-ui | No | Treat 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. |
--volume | No | Attach a persistent named volume mounted at /data in the container. |
--wp-env=K=V,K=V | No | Comma-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(default7000to7999). 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 fromrepositories.json.get_docker_compose_cmdmerges 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:
wdg my-site start # starts WordPress and its sidecars
wdg my-site stop # stops WordPress and its sidecars
wdg my-site restart # restarts bothThey 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
wdg my-site service listLists 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.
wdg my-site service remove typesense # keeps the data volume
wdg my-site service remove typesense --purge # also deletes the data volumeRemoving 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_cmdso the sidecars file is merged in. Re-runwdg my-site start(orwdg my-site restart). - UI port conflict / "No free host port". Host ports are allocated from
SIDECAR_PORT_RANGE_START..END(default7000to7999). If the range is exhausted, widen it in.env. Keep it disjoint from the6xxxplatform ports (nginx, MySQL, Qdrant, MCP server) to avoid collisions. - Sidecar disappeared after
repo addordoctor --fix. Sidecar state must live indocker-compose.sidecars.yml. The repo-mount system owns and regeneratesdocker-compose.override.yml, so anything misplaced there is lost. Re-add withwdg my-site service add.
Database Commands
Export Database
wdg my-site db export [filename]Examples:
# Auto-generated, timestamped filename
wdg my-site db export
# Custom filename
wdg my-site db export backup-$(date +%Y%m%d).sqlImport Database
wdg my-site db import <sql-file>wdg my-site db import production-db.sql⚠️ WARNING
Importing replaces existing data in the project database.
Reset Database
wdg my-site db resetResets the database to a fresh WordPress installation.
Search & Replace
wdg my-site db search-replace <search> <replace>Examples:
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
wdg my-site sync <pantheon|wpvip|wpengine|kinsta> [options]Pulls the database (and optionally media) from a hosting platform.
Examples:
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 --mediaSee Connecting hosting platforms for WP Engine and Kinsta setup.
WordPress CLI
wdg my-site wp <wp-cli-command>Runs WP-CLI inside the project container (auto-starting the project if it is stopped).
Examples:
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
wdg my-site indexIndexes the project's repositories into its project_<name> vector collection. See All CLI Commands for wdg index --platform and Wikit indexing.
Logs
wdg my-site logs [type] [options]View project logs by type: debug, php, nginx, container, all.
Options:
--follow/-f: follow live--tail=N: lastNlines--since=<time>: entries since a time--grep=<pattern>: filter by pattern
Examples:
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 errorsProject Manifest
The manifest records project metadata (client, Jira project, hosting platform, etc.).
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 $EDITORmanifest 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.
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 storeSkills & AI Config
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 + agentsThe org-wide forms (wdg skills, wdg ai-config) are documented in All CLI Commands.
Playwright
wdg my-site playwright # project-scoped Playwright browser automationCommon Workflows
Daily Development
wdg my-site start
wdg my-site repo sync
wdg my-site dev
# ... make changes, git commit (triggers auto-indexing) ...
wdg my-site stopSwitching PHP Versions
wdg my-site php # check current version
wdg my-site php set 8.3 # change (container rebuilds automatically)
wdg my-site php # verifyProduction Build Process
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: