# Using the `ddev` Command

Type `ddev help` or `ddev --help` or `ddev -h` in a terminal window to see the available DDEV [commands](<https://ddev.github.io/ddev/en/stable/users/usage/commands/index.md>). There are commands to configure a project, start, stop, describe, etc. Each command also has help using `ddev help <command>` or `ddev command -h`. For example, `ddev help snapshot` will show help and examples for the snapshot command.

## Interactive Dashboard

Running `ddev` with no arguments launches an interactive terminal dashboard. The dashboard shows all your DDEV projects and their status, and lets you manage them with these keyboard shortcuts.

### Navigation

| Key | Action |
| --- | --- |
| <kbd>Enter</kbd> | Open project detail view |
| <kbd>↑</kbd> / <kbd>↓</kbd> or <kbd>k</kbd> / <kbd>j</kbd> | Move the selection, or scroll the detail and log views |
| <kbd>PgUp</kbd>/<kbd>b</kbd> / <kbd>PgDn</kbd>/<kbd>f</kbd>, <kbd>Home</kbd>/<kbd>g</kbd> / <kbd>End</kbd>/<kbd>G</kbd> | Scroll the list, detail or logs a screen at a time, or jump to the start or end |
| <kbd>u</kbd> / <kbd>d</kbd> | Scroll the detail or logs half a screen up or down |
| <kbd>Esc</kbd> / <kbd>Backspace</kbd> | Go back |
| <kbd>/</kbd> | Filter projects by name, type, status, or directory name; <kbd>Enter</kbd>, <kbd>Tab</kbd>, <kbd>↑</kbd> or <kbd>↓</kbd> ends typing, and <kbd>Esc</kbd> clears the filter |
| <kbd>r</kbd> | Refresh |
| <kbd>?</kbd> | Show or hide the full list of keys |
| <kbd>q</kbd> | Quit |

### Projects

| Key | Action |
| --- | --- |
| <kbd>s</kbd> / <kbd>S</kbd> | Start / Stop the project |
| <kbd>R</kbd> | Restart the project |
| <kbd>D</kbd> | Delete the project and its database, like [`ddev delete`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#delete>); confirm with <kbd>y</kbd>, or <kbd>O</kbd> to skip the snapshot |
| <kbd>l</kbd> | Launch project URL in browser |
| <kbd>m</kbd> | Launch Mailpit in browser |
| <kbd>x</kbd> | Launch XHGui in browser |
| <kbd>o</kbd> | Open project directory in the file manager |
| <kbd>v</kbd> / <kbd>p</kbd> | Open project in VS Code / PhpStorm |
| <kbd>e</kbd> | SSH into web container (detail view) |
| <kbd>L</kbd> | Follow logs (detail view) |
| <kbd>X</kbd> | Toggle Xdebug (detail view) |
| <kbd>c</kbd> | Copy the project URL (detail view) |
| <kbd>n</kbd> | Make a database snapshot, like [`ddev snapshot`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#snapshot>), while the project runs (detail view) |
| <kbd>N</kbd> | Restore the latest snapshot, like [`ddev snapshot restore --latest`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#snapshot-restore>); confirm with <kbd>y</kbd> (detail view) |
| <kbd>U</kbd> | Update the installed add-ons that have a newer release, like [`ddev add-on update`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#add-on-update>); confirm with <kbd>y</kbd> (detail view) |
| <kbd>a</kbd> / <kbd>A</kbd> | Start all / Stop all projects; confirm with <kbd>y</kbd> |
| <kbd>P</kbd> | Poweroff all DDEV projects and containers; confirm with <kbd>y</kbd> |
| <kbd>C</kbd> | Run `ddev config` interactively in the current directory |

Commands such as start and delete run in the terminal itself, so their output, errors included, stays in the terminal after you quit the dashboard.

In terminals that support [OSC 8 hyperlinks](<https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>), project paths and URLs in the dashboard and detail view are clickable, as are the VS Code and PhpStorm links in the detail view. The editor links use the `vscode://` and `phpstorm://` URL schemes, so they work only when that editor is installed, and some terminals block those schemes. PhpStorm does not register `phpstorm://` on Windows, so there the PhpStorm link needs a separately installed handler. The <kbd>p</kbd> key avoids this by using PhpStorm’s command-line launcher when it is on your `PATH`, which [JetBrains Toolbox](<https://www.jetbrains.com/toolbox-app/>) can install.

To disable the dashboard and show the classic help text instead, set [`no_tui: true`](<https://ddev.github.io/ddev/en/stable/users/configuration/config/#no_tui>) in your global configuration (`$HOME/.ddev/global_config.yaml`), or set the environment variable `DDEV_NO_TUI=true`.

### Terminal Compatibility

The dashboard automatically detects your terminal’s color capabilities and adapts its output. If colors or styling don’t render correctly in your terminal, you have several options:

- **Disable colors**: Run `NO_COLOR=1 ddev` or `export NO_COLOR=1` in your shell to disable all color output while keeping the dashboard functional.
- **Use simple formatting globally**: Run `ddev config global --simple-formatting=true` to disable colors and table formatting across all DDEV commands, including the dashboard. See [`simple_formatting`](<https://ddev.github.io/ddev/en/stable/users/configuration/config/#simple_formatting>).
- **Set `TERM` appropriately**: If your terminal supports color but DDEV doesn’t detect it, make sure your `TERM` environment variable is set correctly (e.g., `xterm-256color`). Some SSH clients or multiplexers like `tmux` or `screen` may need explicit configuration.
- **Disable the dashboard entirely**: If the interactive dashboard doesn’t work well in your environment (e.g., a very limited terminal or CI), use `DDEV_NO_TUI=true` or set [`no_tui: true`](<https://ddev.github.io/ddev/en/stable/users/configuration/config/#no_tui>) in `$HOME/.ddev/global_config.yaml` to fall back to the classic help output.
- [`ddev config`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#config>) configures a project’s type and docroot, either interactively or with flags.
- [`ddev start`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#start>) starts up a project.
- [`ddev launch`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#launch>) opens a web browser showing the project.
- [`ddev list`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#list>) shows current projects and their state.
- [`ddev describe`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#describe>) gives all the info about the current project.
- [`ddev ssh`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#ssh>) takes you into the web container.
- [`ddev exec <command>`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#exec>) executes a command inside the web container.
- [`ddev stop`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#stop>) stops a project and removes its memory usage (but does not throw away any data).
- [`ddev poweroff`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#poweroff>) stops all resources that DDEV is using and stops the Mutagen daemon if it’s running.
- [`ddev delete`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#delete>) destroys the database and DDEV’s knowledge of the project without touching your code.
- [`ddev add-on`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#add-on>) adds an add-on service.

## Lots of Other Commands

- [`ddev mysql`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#mysql>) gives direct access to the MySQL client and [`ddev psql`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#psql>) to the PostgreSQL `psql` client.
- [`ddev sequelace`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#sequelace>), [`ddev tableplus`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#tableplus>), [`ddev tablepro`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#tablepro>), and [`ddev querious`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#querious>) (macOS only, if the app is installed) give access to the Sequel Ace, TablePlus, TablePro or Querious database browser GUIs.
- [`ddev dbeaver`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#dbeaver>) (if installed) gives access to the DBeaver database browser GUI.
- [`ddev heidisql`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#heidisql>) (Windows/WSL2/Linux, if installed) gives access to the HeidiSQL database browser GUI.
- [`ddev import-db`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#import-db>) and [`ddev export-db`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#export-db>) import or export SQL or compressed SQL files.
- [`ddev composer`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#composer>) runs Composer inside the container. For example, `ddev composer install` will do a full composer install for you without even needing Composer on your computer. See [developer tools](<https://ddev.github.io/ddev/en/stable/users/usage/developer-tools/#ddev-and-composer>).
- [`ddev snapshot`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#snapshot>) makes a very fast snapshot of your database that can be easily and quickly restored with [`ddev snapshot restore`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#snapshot-restore>).
- [`ddev share`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#share>) requires ngrok and at least a free account on [ngrok.com](<https://ngrok.com>) so you can let someone in the next office or on the other side of the planet see your project and what you’re working on. `ddev share -h` gives more info about how to set up ngrok.
- [`ddev xdebug`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#xdebug>) enables Xdebug, `ddev xdebug off` disables it, and `ddev xdebug status` shows status. You can toggle Xdebug on and off easily using `ddev xdebug toggle`.
- [`ddev xhprof`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#xhprof>) enables xhprof, `ddev xhprof off` disables it, and `ddev xhprof status` shows status.
- [`ddev xhgui`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#xhgui>) enables XHGui, `ddev xhgui off` disables it, and `ddev xhgui status` shows status.
- [`ddev dr`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#dr>) (Drupal 11.4+ only) gives direct access to the `dr` CLI, the next-generation Drupal CLI available in Drupal 11.4+.
- [`ddev drush`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#drush>) (Drupal and Backdrop only) gives direct access to the `drush` CLI.
- [`ddev artisan`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#artisan>) (Laravel only) gives direct access to the Laravel `artisan` CLI.
- [`ddev asterios`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#asterios>) (Asterios only) gives direct access to the Asterios `asterios` CLI.
- [`ddev magento`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#magento>) (Magento2 only) gives access to the `magento` CLI.
- [`ddev maho`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#maho>) (Maho only) gives direct access to the `maho` CLI.
- [`ddev console`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#console>) (Symfony and Shopware only) gives access to the `symfony console` CLI.
- [`ddev craft`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#craft>) (Craft CMS only) gives access to the `craft` CLI.
- [`ddev npm`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#npm>) gives direct access to the `npm` CLI.
- [`ddev npx`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#npx>) gives direct access to the `npx` CLI.
- [`ddev yarn`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#yarn>) gives direct access to the `yarn` CLI.
- [`ddev cake`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#cake>) (CakePHP only) gives direct access to the `cake` CLI.
- [`ddev sake`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#sake>) (Silverstripe CMS only) gives direct access to the `sake` CLI.
- [`ddev spark`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#spark>) (CodeIgniter) gives direct access to the `spark` CLI.
- [`ddev typo3`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#typo3>) (TYPO3 only) gives direct access to the `typo3` CLI.
- [`ddev wp`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#wp>) (WordPress and Bedrock only) gives direct access to the `wp` CLI.

## Node.js Tools

`node`, `nodejs`, `npm`, `npx` and `yarn` are preinstalled in the web container. You can configure the default value of the installed Node.js version with the [`nodejs_version`](<https://ddev.github.io/ddev/en/stable/users/configuration/config/#nodejs_version>) option in `.ddev/config.yaml` or with `ddev config --nodejs-version`. There is also a [`ddev yarn`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#yarn>) command.

## More Bundled Tools

In addition to the [*commands*](<https://ddev.github.io/ddev/en/stable/users/usage/commands/index.md>) listed above, there are lots of tools included inside the containers:

- [`ddev describe`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#describe>) tells how to access **Mailpit**, which captures email in your development environment.
- Composer, Git, Node.js, npm, symfony, and dozens of other tools are installed in the web container, and you can access them via [`ddev ssh`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#ssh>) or [`ddev exec`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#exec>).
- [`ddev logs`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#logs>) gets you web server logs; `ddev logs -s db` gets database server logs.
- `sqlite3` and the `mysql` and `psql` clients are inside the web container (and `mysql` or `psql` client is also in the `db` container).

## Exporting a Database

You can export a database with [`ddev export-db`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#export-db>), which outputs to stdout or with options to a file:

```
ddev export-db --file=/tmp/db.sql.gz
ddev export-db --file=/tmp/db.sql
ddev export-db --compression=zstd >/tmp/db.sql.zst
ddev export-db >/tmp/db.sql.gz
```

## `ddev import-files`

To import static file assets for a project, such as uploaded images and documents, use the command [`ddev import-files`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#import-files>). This command will prompt you to specify the location of your import asset, then import the assets into the project’s upload directory. To define a custom upload directory, set the [`upload_dirs`](<https://ddev.github.io/ddev/en/stable/users/configuration/config/#upload_dirs>) config option. If no custom upload directory is defined, the default will be used:

- For Backdrop projects, this is the `files` directory.
- For Craft CMS projects, this is the `files` directory.
- For Drupal projects, this is the `sites/default/files` directory.
- For Magento 1 projects, this is the `media` directory.
- For Magento 2 projects, this is the `media` directory.
- For Shopware projects, this is the `media` directory.
- For Silverstripe CMS projects, this is the `assets` directory.
- For TYPO3 projects, this is the `fileadmin` directory.
- For WordPress projects, this is the `wp-content/uploads` directory.
- For WordPress Bedrock projects, this is the `app/uploads` directory.

Other project types need a custom configuration to be able to use this command.

```
$ ddev import-files
Provide the path to the source directory or archive you wish to import.
Please note: if the destination directory exists, it will be emptied and replaced with the
import assets specified here.
Path to file(s): ~/workspace/d10/.tarballs/files.tgz
You provided an archive. Do you want to extract from a specific path in your
archive? You may leave this blank if you wish to use the full archive contents.
Archive extraction path:
Successfully imported files for d10
```

`ddev import-files` supports the following file types: `.tar`, `.tar.gz`, `.tar.xz`, `.tar.bz2`, `.tar.zst`, `.tgz`, or `.zip`.

It can also import a directory containing static assets.

If using a Tar or ZIP archive, the archive should contain *only the files inside the files directory*. For example in a Drupal site with files at `sites/default/files`, the archive should only contain the contents of the `files` directory; it should not contain a single `files` directory. To do this with the `tar` command, `cd` into the directory and create the archive there. An example Tar file creation is:

```
cd web/sites/default/files
tar -czf ~/Downloads/files.tgz .
```

If you want to use `import-files` without answering prompts, use the `--source` or `-s` flag to provide the path to the import asset. If you’re importing an archive, and wish to specify the path within the archive to extract, you can use the `--extract-path` flag in conjunction with the `--source` flag. Example:

`ddev import-files --source=/tmp/files.tgz`

When multiple `upload_dirs` are defined and you want to import to another upload dir than the first one, use the `--target` or `-t` flag to provide the path to the desired upload dir:

`ddev import-files --target=../private --source=/tmp/files.tgz`

See `ddev help import-files` for more examples.

## Snapshotting and Restoring a Database

The project database is stored in a Docker volume, but can be snapshotted (and later restored) with the [`ddev snapshot`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#snapshot>) command. A snapshot is automatically taken when you run [`ddev stop --remove-data`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#stop>). For example:

```
ddev snapshot
Creating database snapshot d9_20220107124831-mariadb_10.3.gz
Created database snapshot d9_20220107124831-mariadb_10.3.gz

ddev snapshot restore d9_20220107124831
Stopping db container for snapshot restore of 'd9_20220107124831-mariadb_10.3.gz'...
Restored database snapshot d9_20220107124831-mariadb_10.3.gz
```

Snapshots are stored as gzipped files in the project’s `.ddev/db_snapshots` directory, and the file created for a snapshot can be renamed as necessary. For example, if you rename the above `d9_20220107124831-mariadb_10.3.gz` file to `working-before-migration-mariadb_10.3.gz`, then you can use `ddev snapshot restore working-before-migration`. (The description of the database type and version—`mariadb_10.3`, for example—must remain intact.) To restore the latest snapshot add the `--latest` flag (`ddev snapshot restore --latest`).

List snapshots for an existing project with `ddev snapshot --list`. (Add the `--all` option for an exhaustive list; `ddev snapshot --list --all`.) You can remove all of them with `ddev snapshot --cleanup`, or remove a single snapshot with `ddev snapshot --cleanup --name <snapshot-name>`.

> [!TIP]
>
> The default 120-second timeout may be inadequate for restores with very large snapshots or slower systems. You can increase this timeout by setting [`default_container_timeout`](<https://ddev.github.io/ddev/en/stable/users/configuration/config/#default_container_timeout>) to a higher value.
>
> A timeout doesn’t necessarily mean the restore failed; you can watch the snapshot restore complete by running `ddev logs -s db`.

## Interacting with Your Project

DDEV provides several commands to facilitate interacting with your project in the development environment. These commands can be run within the working directory of your project while the project is running in DDEV.

### Executing Commands in Containers

The [`ddev exec`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#exec>) command allows you to run shell commands in the container for a DDEV service. By default, commands are executed on the web service container, in the docroot path of your project. This allows you to use [the developer tools included in the web container](<https://ddev.github.io/ddev/en/stable/users/usage/developer-tools/index.md>). For example, to run the `ls` command in the web container, you would run `ddev exec ls` or `ddev . ls`.

To run a shell command in the container for a different service, use the `--service` (or `-s`) flag at the beginning of your `exec` command to specify the service the command should be run against. For example, to run the MySQL client in the database, container, you would run `ddev exec --service db mysql`. To specify the directory in which a shell command will be run, use the `--dir` flag. For example, to see the contents of the `/usr/bin` directory, you would run `ddev exec --dir /usr/bin ls`.

To run privileged commands, sudo can be passed into `ddev exec`. For example, to update the container’s apt package lists, use `ddev exec sudo apt-get update`.

Commands can also be executed using the shorter `ddev . <cmd>` alias.

Normally, `ddev exec` commands are executed in the container using Bash, which means that environment variables and redirection and pipes can be used. For example, a complex command like `ddev exec 'ls -l ${DDEV_FILES_DIR} | grep x >/tmp/junk.out'` will be interpreted by Bash and will work. However, there are cases where Bash introduces too much complexity and it’s best to run the command directly. In those cases, something like `ddev exec --raw ls -l "dir1" "dir2"` may be useful. With `--raw`, the `ls` command is executed directly instead of the full command being interpreted by Bash. But you cannot use environment variables, pipes, redirection, etc.

### SSH Into Containers

The [`ddev ssh`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#ssh>) command opens an interactive Bash or sh shell session to the container for a DDEV service. The web service is connected by default, and the session can be ended by typing `exit`. To connect to another service, use the `--service` flag to specify the service you want to connect to. For example, to connect to the database container, you would run `ddev ssh --service db`. To specify the destination directory, use the `--dir` flag. For example, to connect to the database container and be placed into the `/home` directory, you would run `ddev ssh --service db --dir /home`.

You can also use your personal SSH keys within the web container. Run `ddev auth ssh` to add the keys from your `~/.ssh` directory and provide a passphrase, and those keys will be usable from within the web container. You generally only have to `ddev auth ssh` one time per computer reboot. This is a very popular approach for accessing private Composer repositories, or for using `drush` aliases against remote servers.

If your keys live in an SSH agent rather than in `~/.ssh`, as with 1Password or a hardware key, see [Using an Existing SSH Agent](<#using-an-existing-ssh-agent>).

### Using an Existing SSH Agent

If you already use an SSH agent, such as the macOS agent, 1Password, Secretive, `gpg-agent` with a YubiKey, or one forwarded with `ssh -A`, DDEV’s containers can use it directly. Set this once:

```
ddev config global --ssh-agent-upstream=host
ddev auth ssh      # lists the keys your containers can use
```

After that, every project’s containers use your agent when they start, with nothing to add per project. `ddev auth ssh` only shows which keys are available, and `ddev exec ssh -T git@github.com` tests them. Every container on the DDEV network can ask your agent to sign while this is on; agents that confirm each use, like 1Password, limit that exposure.

If `ddev auth ssh` says “Make sure that agent is running and holds your keys”, your agent is stopped or locked; open it or load your keys, and DDEV picks it up again without a restart. To go back to adding key files, run `ddev config global --ssh-agent-upstream=""` and then `ddev auth ssh`.

If a connection instead fails with “Too many authentication failures”, the server capped how many keys it will try, and relaying a whole agent offers more than the few keys you’d add by hand; see [Too Many Authentication Failures](<https://ddev.github.io/ddev/en/stable/users/extend/in-container-configuration/#too-many-authentication-failures>) to limit which key gets offered per host.

> [!TIP]
>
> **macOS: which agent do my containers get?**
>
> That depends on your Docker provider:
>
> | Provider | What to do | Agent containers get |
> | --- | --- | --- |
> | OrbStack | Nothing extra | The agent named by `IdentityAgent` in `~/.ssh/config`, such as 1Password, otherwise the macOS agent. Restart OrbStack after changing `~/.ssh/config`. |
> | Docker Desktop | Nothing extra | Always the macOS agent |
> | Colima | Start it with `colima start --ssh-agent` | Always the macOS agent |
> | Lima | Run `limactl edit <instance> --set .ssh.forwardAgent=true` once | Always the macOS agent |
> | Rancher Desktop, Podman | Not supported | DDEV warns and uses its own agent; add key files with `ddev auth ssh` as usual |
>
> To use 1Password, Secretive, or `gpg-agent`:
>
> - With OrbStack, set `IdentityAgent` in `~/.ssh/config` as your agent’s setup instructions describe, then restart OrbStack.
> - Docker Desktop, Colima, and Lima ignore `IdentityAgent`. Point the system-wide `SSH_AUTH_SOCK` at your agent as its documentation describes; for 1Password, see [Configure SSH\_AUTH\_SOCK globally for every client](<https://developer.1password.com/docs/ssh/agent/compatibility/>).
> - Exporting `SSH_AUTH_SOCK` in your shell profile doesn’t reach any of these providers.
>
> To use the macOS agent, load your keys into it, saving the passphrase in the Keychain:
>
> ```
> ssh-add --apple-use-keychain ~/.ssh/id_ed25519
> ssh-add -l
> ```

> [!TIP]
>
> **Linux: pointing at your own agent**
>
> `host` uses the agent in `$SSH_AUTH_SOCK` when a project starts. A forwarded agent (`ssh -A`) gets a new socket for each login, so run `ddev auth ssh` again after logging in again. When the socket is a symlink, as with VS Code Remote’s `/tmp/vscode-ssh-auth-sock-*`, DDEV uses the socket it points to. After VS Code or SSH reconnects, a terminal opened earlier still points to the old, dead socket, so run `ddev auth ssh` from a new terminal. A desktop agent’s socket doesn’t change, so you can set it directly, for example on Ubuntu with `ddev config global --ssh-agent-upstream=/run/user/$(id -u)/gcr/ssh`. An agent you start yourself with `eval $(ssh-agent -s)` works when you run `ddev` from that shell; to use it from any shell, start it on a fixed socket with `mkdir -p -m 700 ~/.ssh-agent && eval $(ssh-agent -a ~/.ssh-agent/agent.sock -s)` and set `ddev config global --ssh-agent-upstream=$HOME/.ssh-agent/agent.sock`. Keep the socket in its own directory rather than `~/.ssh`, because DDEV mounts the socket’s whole directory into the `ddev-ssh-agent` container.

> [!TIP]
>
> **Docker Desktop: using an agent forwarded with `ssh -A`**
>
> Docker Desktop relays the agent in the `SSH_AUTH_SOCK` it started with, not your shell’s, and a forwarded agent gets a new socket for each login. To follow each new login without restarting Docker Desktop, start it once on a fixed path and point that path at each login’s socket from `~/.ssh/rc`, which `sshd` runs at login:
>
> ```
> # Once. On macOS, use 'launchctl setenv SSH_AUTH_SOCK ...' and 'docker desktop restart' instead.
> mkdir -p -m 700 ~/.ssh-agent-forward
> systemctl --user set-environment SSH_AUTH_SOCK=$HOME/.ssh-agent-forward/agent.sock
> systemctl --user restart docker-desktop
>
> # In ~/.ssh/rc:
> if [ -n "$SSH_AUTH_SOCK" ]; then ln -sf "$SSH_AUTH_SOCK" ~/.ssh-agent-forward/agent.sock; fi
> ```
>
> The most recent `ssh -A` login wins, and after it ends, containers can’t reach an agent until you log in again. With X11 forwarding, `~/.ssh/rc` also has to run `xauth`, as `man sshd` describes. The Docker Desktop setting doesn’t last past a reboot.

> [!TIP]
>
> **WSL2: bridging a Windows agent**
>
> DDEV can’t reach Windows agents such as 1Password and the Windows OpenSSH agent directly, because they listen only on a Windows named pipe. Bridge that pipe to a Unix socket with `socat` in WSL2 and [`npiperelay`](<https://github.com/albertony/npiperelay>) on Windows, then point DDEV at the socket. DDEV doesn’t start the bridge for you. This works with Docker Desktop’s WSL integration as well as Docker installed inside WSL2, although Docker Desktop on Windows doesn’t forward an agent of its own.
>
> 1. Install `socat` in WSL2 with `sudo apt install socat`, and `npiperelay` in PowerShell with `winget install albertony.npiperelay`. `npiperelay` is x64-only; Windows on Arm runs it under emulation.
> 2. With [systemd enabled in WSL2](<https://learn.microsoft.com/en-us/windows/wsl/systemd>), run the bridge as a user service, which starts with WSL2 whether or not you open a shell. Run this in a WSL2 shell, where `npiperelay.exe` is on `PATH`; the service itself doesn’t get the Windows `PATH`, so the unit records the full path:
>
>    ```
>    mkdir -p ~/.config/systemd/user
>    cat > ~/.config/systemd/user/ssh-agent-bridge.service <<EOF
>    [Unit]
>    Description=Bridge the Windows SSH agent to ~/.1password/agent.sock
>
>    [Service]
>    ExecStartPre=/usr/bin/mkdir -p -m 700 %h/.1password
>    ExecStart=/usr/bin/socat UNIX-LISTEN:%h/.1password/agent.sock,fork,unlink-early EXEC:"$(command -v npiperelay.exe) -ei -s //./pipe/openssh-ssh-agent",nofork
>    Restart=on-failure
>    SuccessExitStatus=143
>
>    [Install]
>    WantedBy=default.target
>    EOF
>    systemctl --user enable --now ssh-agent-bridge
>    ```
>
>    Without systemd, add this to `~/.bashrc` instead, so that each new shell starts the bridge if it isn’t already running:
>
>    ```
>    export SSH_AUTH_SOCK=$HOME/.1password/agent.sock
>    ssh-add -l >/dev/null 2>&1
>    if [ $? -eq 2 ]; then
>      rm -f "$SSH_AUTH_SOCK"
>      mkdir -p -m 700 "$(dirname "$SSH_AUTH_SOCK")"
>      (setsid socat UNIX-LISTEN:"$SSH_AUTH_SOCK",fork EXEC:"npiperelay.exe -ei -s //./pipe/openssh-ssh-agent",nofork &) >/dev/null 2>&1
>    fi
>    ```
> 3. Check that `SSH_AUTH_SOCK=~/.1password/agent.sock ssh-add -l` lists your keys, then run:
>
>    ```
>    ddev config global --ssh-agent-upstream=$HOME/.1password/agent.sock
>    ddev auth ssh
>    ```
>
> To use the same agent for `ssh` and `git` in WSL2, add `export SSH_AUTH_SOCK=$HOME/.1password/agent.sock` to `~/.bashrc`. If the bridge stops, containers can’t use your keys until it runs again, and nothing in DDEV needs a restart.

> [!TIP]
>
> **Traditional Windows: key files, or piping from 1Password**
>
> `ssh_agent_upstream` doesn’t work here. Windows agents, including 1Password and the Windows OpenSSH agent, listen on a named pipe that Docker Desktop and other Windows providers can’t pass to containers. DDEV warns and uses its own agent, so add your key files from `%USERPROFILE%\.ssh` with `ddev auth ssh`. To use a key stored in 1Password without saving it to a file, pipe it from the [1Password CLI](<https://developer.1password.com/docs/cli/get-started/>), in PowerShell or Git Bash:
>
> ```
> op read "op://<vault>/<item>/private key?ssh-format=openssh" | ddev auth ssh -f -
> ```
>
> Turn on **Settings** \> **Developer** \> **Integrate with 1Password CLI** in the 1Password app first, and add `--account <account>.1password.com` to `op read` if you’re signed in to more than one account. Run the command again after `ddev poweroff` or a reboot, because DDEV’s agent forgets keys when it stops.

### `ddev logs`

The [`ddev logs`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#logs>) command allows you to easily view error logs from the web container (both nginx/Apache and php-fpm logs are concatenated). To follow the logs in real time, run `ddev logs -f`. When you’re done, press <kbd>CTRL</kbd> + <kbd>C</kbd> to exit the log trail. Similarly, `ddev logs -s db` will show logs from a running or stopped database container.

## Stopping a Project

To remove a project’s containers, run [`ddev stop`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#stop>) in the project’s working directory. To remove any running project’s containers regardless of context, specify the project name as an argument: `ddev stop <projectname>`.

`ddev stop` is *not* destructive. It removes the Docker containers but does not remove the database for the project, and does nothing to your code. This allows you to have many configured projects with databases loaded without wasting Docker containers on unused projects. **`ddev stop` does not affect the project codebase and files.**

To remove the imported database for a project, use the flag `--remove-data`, as in `ddev stop --remove-data`. This command will destroy both the containers and the imported database contents.
