DDEV Operations
SkillDatabases & dataUse when a project has a .ddev/ folder or needs one: DDEV setup on a new machine, version pinning, config.local.yaml and .ddev/.env files, database import, snapshots, ddev pull and sanitised data, slow Mutagen or WSL2 sites, add-ons (Redis, Solr, cron), commands and hooks, Vite ports, Xdebug, ddev share, port conflicts, several git worktrees at once, scripting DDEV (describe -j), disk cleanup, upgrades and troubleshooting. Audits .ddev/ for team-breaking mistakes.
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; ahel provides instructions and does not run this skill.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use the DDEV Operations skill
What this skill tells your AI
The instructions your AI receives, as published by 0xdarkmatter/claude-mods in skills/ddev-ops/SKILL.md and read by ahel’s review.
Why: DDEV is the local stack behind most PHP agency sites, and it fails quietly: a default that moved under an unpinned project, a key it silently ignores, one developer's setting committed for the whole team.
Versions verified 2026-10-05 against DDEV v1.25.4. Fast-moving facts (defaults, PHP and Node.js end-of-life floors, built-in command names) live once in
assets/ddev-facts.json;scripts/check-ddev-facts.py --offlinefails if this skill's prose and that file disagree, and--live(weekly) fails when DDEV moves.
Start here
-
Read the project before changing it:
.ddev/config.yaml, anyconfig.*.yaml,ls -a .ddev/, thenddev describe(URLs, ports, services) andddev version. -
Audit it. From the skill folder (the launcher finds a working Python 3.8+, which matters on Windows where
python3is often a Store stub):bash scripts/run-python.sh scripts/audit-ddev-config.py /path/to/project bash scripts/run-python.sh scripts/audit-ddev-config.py /path/to/project --json | jq '.data[]'Exit 10 means findings, sorted high to low (severity, check, file, detail); 0 means clean.
--ignore <check>documents a deliberate exception. -
Run everything through DDEV -
ddev composer,ddev php,ddev npm,ddev exec- so commands use the container's PHP and Node, not the host's. -
Snapshot before anything risky, with a unique name:
ddev snapshot --name=before-<task>-$(date +%Y%m%d%H%M). -
Ask before destroying data (Hard rule 11).
Hard rules
Rules 2, 3, 4, 6, 8 and 12 were each seen in a 2026-10-05 read of 36 DDEV-based agency repositories; the rest are quiet failures DDEV's own docs and source describe.
-
Pin
php_versionanddatabaseto production. Unpinned projects follow DDEV's defaults, which move: v1.25.0 switched PHP 8.3 to 8.4 and MariaDB 10.11 to 11.8. Also pinnodejs_versionandcomposer_version: "2". -
Per-developer settings never go in
config.yaml.- That means
performance_mode,router_http_port/router_https_port,xdebug_enabled: true,bind_all_interfacesandhost_db_port. - They belong in
.ddev/config.local.yaml(gitignored) orddev config global. - Project values beat global ones, so a committed value pins every teammate.
- That means
-
Retired keys do nothing. DDEV loads config non-strictly:
mutagen_enabled: falseandnfs_mount_enabledare ignored, not obeyed. -
upload_dirsresolve from the docroot. Withdocroot: web,storagemeansweb/storage; folders beside the docroot are../storageand../node_modules. Setting the list replaces the type's defaults; override files append to it. -
No secrets in committed
.ddev/.env*files. Use the.localtwin (.ddev/.env.web.local, gitignored since v1.25.4). Commit a.examplewithgit add -f, since DDEV's.gitignorehides*.example. -
Never forward the host SSH agent into containers. Every process in them - Composer and npm scripts included - could sign with every key the agent holds. Load one scoped key with
ddev auth ssh -f <key>. -
Pull sanitised data; never push to production.
- Anonymise where the data lives and pull the result. No raw production personal data on laptops.
- Delete
db_push_command/files_push_commandfrom your own recipes that can reach production. - A
files_pull_commandthat fetches nothing makesddev pullempty the upload directory. Omit the stanza instead.
-
Don't keep project copies of DDEV's built-in commands (
craft,npm,artisan,wp...). A project command shadows the built-in and freezes an old version. Command files must have LF endings: DDEV skips CRLF ones with only a warning. -
Review an add-on before installing it. Its Bash install actions run on your machine. Prefer official
ddev/add-ons and pin--version. -
On Windows, keep projects inside the WSL2 filesystem, never under
/mnt/c. -
Confirm with the user before destroying data. These destroy data:
ddev delete,ddev stop --remove-data,ddev start --reset-database;ddev snapshot --cleanup,ddev clean,ddev push;ddev import-dbandddev import-files, which empty their target first;docker volume prune -a, which deletes stopped projects' databases.
ddev poweroffstops everyone's projects. -
Several checkouts need distinct project names. A committed
name:makes every git worktree claim the same project. Omit it, or override it inconfig.local.yaml.
Driving DDEV from a script or agent
- Never run bare
ddev(it opens a dashboard) or flaglessddev config(it asks questions). Pass--file/--sourceto imports and-yto anything that prompts. - Read state as JSON:
ddev describe -j | jq -r '.raw.primary_url'(also.raw.status,.raw.dbinfo.published_port);ddev list -j;ddev launch --print-url. - Single-quote
ddev execcommands that contain$, pipes or redirects, so they expand inside the container.
Details, the destroys-data table and worktree setup: automation-and-worktrees.md.
Route by task
| Task | Read |
|---|---|
| Existing repo on a new machine, new project, version pinning, CI parity, Composer, what to commit, env files, upgrading DDEV | config-and-env.md |
| Scripting DDEV, JSON output, commands that destroy data, several git worktrees, renaming or moving a project | automation-and-worktrees.md |
Import/export, snapshots, seed and reset, changing engine, ddev pull recipes, sanitised data | database.md |
Slow project, Mutagen, upload_dirs, WSL2, choosing a Docker provider | performance.md |
| Add-ons (Redis, search, cron), commands, hooks, daemons, nginx/Apache config, extra services, networking between projects, PHP ini, SSH keys | extending.md |
| Node.js version, npm/pnpm/yarn, dev-server ports and daemons | frontend-node.md |
| Craft + Vite HMR not connecting (CORS, allowed hosts, the port in four places) | frontend-upgrade-ops' references/craft-vite-twig.md |
Xdebug, XHGui profiling, Mailpit, ddev share, hostnames | debugging-sharing.md |
| Something is broken: Docker, ports, router, TLS, disk, DNS, Mutagen | troubleshooting.md |
Everyday commands
| Task | Command |
|---|---|
| Start, stop, restart, everything off | ddev start, ddev stop, ddev restart, ddev poweroff |
| What is running and where | ddev describe (-j for JSON), ddev list, ddev launch |
| Shell, one-off command | ddev ssh, ddev exec <cmd> |
| Logs | ddev logs, ddev logs -s db, ddev logs -f |
| Database in and out | ddev import-db --file=x.sql.gz, ddev export-db --file=x.sql.gz |
| Snapshot and restore | ddev snapshot --name=<unique>, ddev snapshot restore --latest |
| Data from hosting | ddev pull <provider> (--skip-files, --skip-db, -y) |
| Xdebug, profiler | ddev xdebug on (off, status), ddev xhgui on, ddev xhgui launch |
| Mail catcher | ddev mailpit |
| Add-ons | ddev add-on get <owner>/<repo>, ddev add-on list --installed, ddev add-on update |
| Effective config, custom files | ddev utility configyaml --full-yaml, ddev utility check-custom-config |
| Health checks | ddev utility diagnose, port-diagnose, tls-diagnose, mutagen-diagnose, xdebug-diagnose |
| Rebuild images | ddev start --no-cache, ddev utility rebuild |
Scripts and assets
| Resource | Use |
|---|---|
scripts/audit-ddev-config.py [DIR] [--json] [--ignore CHECK] | Read-only audit of .ddev/; details below the table |
scripts/check-ddev-facts.py --offline | After editing facts or prose: assets/ddev-facts.json vs this skill's text |
scripts/check-ddev-facts.py --live | Weekly: newest DDEV release, docs defaults, built-in commands, PHP/Node.js end-of-life floors. Exit 7 = a source was unreachable (advisory) |
scripts/run-python.sh | Launches either script with the first real Python 3.8+ (python3, python, py) |
assets/ddev-facts.json | The facts both scripts share - bump values, prose and the "Versions verified" note together |
| assets/sanitized-pull.yaml.example | Database-only provider recipe for a sanitised dump: no files stanza, no push stanzas |
The auditor:
- Checks:
- versions:
php-unpinned,php-out-of-range,php-eol,db-unpinned,node-eol,composer-v1; - config:
obsolete-key,perf-mode-committed,router-ports-committed,xdebug-committed,name-in-worktree; - folders and commands:
upload-dir-misplaced,upload-dir-outside,shadowed-command,crlf-command; - safety:
ssh-agent-forwarded,provider-push,provider-files-noop,committed-secret.
- versions:
- Exit codes: 0 clean, 10 findings, 3 no
.ddev/config.yaml, 4 unreadable config, 2 usage. - It reads config the way DDEV does: overrides merge, lists append. It skips DDEV's own generated recipes and never prints a secret value.
Boundaries
| Topic | Owner |
|---|---|
Craft on DDEV: ddev craft, .ddev/.env.web CRAFT_* vars, Craft versions to pin, queue, asset upload_dirs | craftcms-ops (references/ddev.md) |
Local-versus-production drift as a security problem: php.ini differences, devMode, keys, mail, ddev share exposure, personal data | security-ops (references/ddev-config-drift.md) |
| craft-vite and Vite HMR config behind DDEV (port in four places, CORS, allowed hosts) | frontend-upgrade-ops (references/craft-vite-twig.md) |
Install commands, lockfiles, .nvmrc/engines agreeing with DDEV's Node and PHP | package-manager-ops |
| Compose syntax for a hand-written extra service; Dockerfiles generally | docker-ops |
| Page speed of the site itself | web-perf-ops |
Never write a real-looking project hostname in examples: say "the project URL from
ddev describe".
Sources
- DDEV docs: docs.ddev.com, source
docs/content/users/at tag v1.25.4. - DDEV release notes: v1.25.0-v1.25.4.
- DDEV source:
pkg/ddevapp/(config.go,provider.go,ddevapp.go,upload_dirs.go,craftcms.go),pkg/settings/viper.goandcmd/ddev/cmd/commands.go. - Also: the add-on registry (addons.ddev.com) and endoflife.date.
Read 2026-10-05 and re-checked by an independent accuracy review on 2026-10-06. DDEV
publishes no agent skill for its users; its own repository's AGENTS.md and .claude/
serve people contributing to DDEV.
Signals
- GitHub stars
- 43
- Forks
- 7
- Last commit
- Sep 2026
ahel review
K6low
bundled executables the agent is told to runK1binfo
installs-packages (in references/config-and-env.md)K1binfo
installs-packages (in references/frontend-node.md)K1binfo
installs-packages (in references/troubleshooting.md)
Automated review, not a security audit. Ruleset v1+k2.
Advanced
- Item type
- skill
- Key
ddev-ops- Source
- github.com/0xdarkmatter/claude-mods
github.com/0xdarkmatter/claude-mods
Related picks
Skill · thedaviddias
The pick for JavaScriptmodern-javascript-patterns
Skill · wshobson
The pick for JavaScriptcraft-php-guidelines
Skill · michtio
The pick for PHPfeature-flags-php
Skill · posthog
The pick for PHPpython-performance-optimization
Skill · wshobson
The pick for Pythonpython-pro
Skill · jeffallan
The pick for Python