Skip to content
Back to skills

Ddev Ops

ASecurity

Use 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.

  • 51 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 6, 2026
ai-agentspythongophpshellbashsqlnodenodejsdockerdebugging

Security analysis

A100/100

Pro scans all 18 files and shows the line behind each finding

Scanned October 7, 2026

npx -y skills add 0xDarkMatter/claude-mods --skill ddev-ops --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ddev Ops?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ddev Ops
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/0xdarkmatter-ddev-ops/badge)](https://www.skillsdirectory.com/skills/0xdarkmatter-ddev-ops)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ddev-ops
description: "Use 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."
license: MIT
allowed-tools: "Read Edit Write Bash Glob Grep"
metadata:
  author: claude-mods
  related-skills: "craftcms-ops, security-ops, frontend-upgrade-ops, docker-ops, web-perf-ops, package-manager-ops"
---

# DDEV Operations

**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 --offline` fails if this skill's
> prose and that file disagree, and `--live` (weekly) fails when DDEV moves.

## Start here

1. **Read the project before changing it:** `.ddev/config.yaml`, any `config.*.yaml`,
   `ls -a .ddev/`, then `ddev describe` (URLs, ports, services) and `ddev version`.
2. **Audit it.** From the skill folder (the launcher finds a working Python 3.8+, which
   matters on Windows where `python3` is often a Store stub):

   ```bash
   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.
3. **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.
4. **Snapshot before anything risky**, with a unique name:
   `ddev snapshot --name=before-<task>-$(date +%Y%m%d%H%M%S)`. A reused name saves
   nothing and still exits 0.
5. **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.

1. **Pin `php_version` and `database` to 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 pin `nodejs_version` and `composer_version: "2"`.
2. **Per-developer settings never go in `config.yaml`.**
   - That means `performance_mode`, `router_http_port`/`router_https_port`,
     `xdebug_enabled: true`, `bind_all_interfaces` and `host_db_port`.
   - They belong in `.ddev/config.local.yaml` or `ddev config global`.
   - Project values beat global ones, so a committed value pins every teammate.
   - Git ignores the `.local` files only after DDEV has run in that checkout, because
     its `.ddev/.gitignore` isn't committed. Add `.ddev/config*.local.y*ml` and
     `.ddev/.env*.local` to the project root's `.gitignore`.
3. **Retired keys do nothing.** DDEV loads config non-strictly:
   `mutagen_enabled: false` and `nfs_mount_enabled` are ignored, not obeyed.
4. **`upload_dirs` resolve from the docroot.** With `docroot: web`, `storage` means
   `web/storage`; folders beside the docroot are `../storage` and `../node_modules`.
   Setting the list replaces the type's defaults; override files append to it.
5. **No secrets in committed `.ddev/.env*` files.** Use the `.local` twin
   (`.ddev/.env.web.local`, v1.25.4+), kept out of git as rule 2 says. Commit a
   `.example` with `git add -f`, since DDEV's `.gitignore` hides `*.example`.
6. **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>`.
7. **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_command` from your own recipes that can reach
     production.
   - A `files_pull_command` that fetches nothing makes `ddev pull` empty the upload
     directory, and the pull still exits 0. Omit the stanza instead.
8. **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.
9. **Review an add-on before installing it.** Its Bash install actions run on your
   machine. Prefer official `ddev/` add-ons and pin `--version`.
10. **On Windows, keep projects inside the WSL2 filesystem**, never under `/mnt/c`.
11. **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-db` and `ddev import-files`, which empty their target first;
    - `docker volume prune -a`, which deletes stopped projects' databases.

    `ddev poweroff` stops everyone's projects.
12. **Several checkouts need distinct project names.** A committed `name:` makes every git
    worktree claim the same project. Omit it, or override it in `config.local.yaml`.

## Driving DDEV from a script or agent

- **Never run bare `ddev`** (it opens a dashboard) **or flagless `ddev config`** (it asks
  questions). Pass `--file`/`--source` to imports and `-y` to 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 exec` commands** that contain `$`, pipes or redirects, so they
  expand inside the container.
- **Exit 0 isn't always success:** a duplicate `ddev snapshot` name and
  `ddev add-on remove` leaving files behind both exit 0.

Details, the destroys-data table and worktree setup:
[automation-and-worktrees.md](references/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](references/config-and-env.md) |
| Scripting DDEV, JSON output, commands that destroy data, several git worktrees, renaming or moving a project | [automation-and-worktrees.md](references/automation-and-worktrees.md) |
| Import/export, snapshots, seed and reset, changing engine, `ddev pull` recipes, sanitised data | [database.md](references/database.md) |
| Slow project, Mutagen, `upload_dirs`, WSL2, choosing a Docker provider | [performance.md](references/performance.md) |
| Add-ons (Redis, search, cron), commands, hooks, daemons, nginx/Apache config, extra services, networking between projects, PHP ini, SSH keys | [extending.md](references/extending.md) |
| Node.js version, npm/pnpm/yarn, dev-server ports and daemons | [frontend-node.md](references/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](references/debugging-sharing.md) |
| Something is broken: Docker, ports, router, TLS, disk, DNS, Mutagen | [troubleshooting.md](references/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](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`, `local-file-committed` (reads git's index; never runs a git or
    fsmonitor command the audited repository supplies).
- **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.go` and
  `cmd/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. What
the references mark "Observed" was reproduced that day in throwaway projects on DDEV
v1.25.4 (Docker Desktop, WSL2). DDEV
publishes no agent skill for its users; its own repository's `AGENTS.md` and `.claude/`
serve people contributing to DDEV.

Files in this skill

  • SKILL.md11.3 KB
  • assets/ddev-facts.json3.4 KB
  • assets/sanitized-pull.yaml.example2.2 KB
  • references/automation-and-worktrees.md6.6 KB
  • references/config-and-env.md13.1 KB
  • references/database.md10.1 KB
  • references/debugging-sharing.md5.3 KB
  • references/extending.md10.3 KB
  • references/frontend-node.md5 KB
  • references/performance.md7 KB
  • references/troubleshooting.md8 KB
  • scripts/audit-ddev-config.py24.3 KB
  • scripts/check-ddev-facts.py18 KB
  • scripts/run-python.sh3 KB
  • tests/fixtures/clean/.ddev/commands/web/db-sanitize222 B
  • tests/fixtures/clean/.ddev/config.local.yaml117 B
  • tests/fixtures/clean/.ddev/config.yaml709 B
  • tests/fixtures/clean/.ddev/providers/staging.yaml388 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.