# Built-in Developer Tools

Run [`ddev describe`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#describe>) to see the project information and services available for your project and how to access them.

## Command-line Tools in the Containers

Hundreds of useful developer tools are included inside the containers and can be used via [`ddev exec`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#exec>) or [`ddev ssh`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#ssh>). Some of those include:

- MySQL client (`mysql`) - Command-line interface for interacting with MySQL/MariaDB.
- PostgreSQL client (`psql`) - Command-line tool for PostgreSQL.
- [PHIVE](<https://github.com/phar-io/phive>) - Command-line tool for “PHAR Installation and Verification Environment”.
- `npm` and `yarn` (these also have `ddev` shortcuts like [`ddev npm`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#npm>) and [`ddev yarn`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#yarn>)).
- `node` (for front-end build tools like [Vite](<https://ddev.github.io/ddev/en/stable/users/usage/vite/index.md>), see the [Vite Integration](<https://ddev.github.io/ddev/en/stable/users/usage/vite/index.md>) documentation)
- `sqlite3`

DDEV also installs some tools only for the project types that need them, such as [Drush](<http://www.drush.org>), [WP-CLI](<http://wp-cli.org/>), [n98-magerun](<https://github.com/netz98/n98-magerun>), and [n98-magerun2](<https://github.com/netz98/n98-magerun2>). See [Project-Specific Utilities](<https://ddev.github.io/ddev/en/stable/users/extend/customizing-images/#project-specific-utilities>) for the full list.

These tools can be accessed for single commands using [`ddev exec <command>`](<https://ddev.github.io/ddev/en/stable/users/usage/cli/#executing-commands-in-containers>) or [`ddev ssh`](<https://ddev.github.io/ddev/en/stable/users/usage/cli/#ssh-into-containers>) for an interactive `bash` or `sh` session.

You can also add tools that are not provided by default using [`webimage_extra_packages` or a custom Dockerfile](<https://ddev.github.io/ddev/en/stable/users/extend/customizing-images/index.md>).

## DDEV and Composer

DDEV provides a built-in command to simplify use of PHP’s dependency manager, [Composer](<https://getcomposer.org/>), without requiring it to be installed on the host machine. Generally, executing any Composer command through DDEV is as simple as prepending the command with `ddev`. DDEV will execute the command at the project root in the web container, passing (almost) all arguments and flags to Composer. To execute Composer in other directories within the container, use `ddev ssh` or `ddev exec -d <dir>`. For example:

- `ddev composer help` runs Composer’s help command to learn more about what’s available.
- `ddev composer require <package>` tells Composer to require a specific PHP package for the current project.

Additionally, Composer can be used to initialize new projects with `ddev composer create-project`. This command supports limited argument and flag options, and will install a new project to the composer root (normally `/var/www/html`). The package and version arguments are required:

`ddev composer create-project [options] [--] [<package> [<directory> [<version>]]]`

For example:

`ddev composer create-project --no-dev typo3/cms-base-distribution . "^9"`

When using `ddev composer create-project` your project should be essentially empty or the command will refuse to run, to avoid loss of your files.

`ddev composer create-project` does these things in this order:

1. `composer create-project --no-plugins --no-scripts --no-install` clones a bare Composer package without any additional actions.
2. `composer run-script post-root-package-install` runs if `--no-scripts` is not given as a flag to `ddev composer create-project`.
3. `composer install` runs if `--no-install` is not given as a flag to `ddev composer create-project`.
4. `composer run-script post-create-project-cmd` runs if `--no-scripts` is not given as a flag to `ddev composer create-project`.

All flags that you provide to `ddev composer create-project` are checked for validity. All invalid flags will be ignored. If they are valid for `composer create-project`, they will be also passed to `composer run-script` and `composer install`, but only if these commands support these flags.

To execute a fully-featured `composer create-project` command, you can execute the command from within the container after executing [`ddev ssh`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#ssh>), or pass the full command to [`ddev exec`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#exec>), like so:

`ddev exec composer create-project ...`

**composer.json Location**: It’s most common for `composer.json` to be in the project root, but you can specify an alternate Composer root using DDEV’s `composer_root` option in `.ddev/config.yaml`, or `ddev config --composer-root <dir>`. The `composer_root` value is the *relative* path from the project root to the directory containing `composer.json`. If yours is at `docroot/composer.json`, for example, the `composer_root` value should be `docroot`.

> [!TIP]
>
> **How to debug Composer itself?**
>
> To use Xdebug with Composer, [see instructions](<https://ddev.github.io/ddev/en/stable/users/debugging-profiling/step-debugging/#composer>).

### Composer Limitations

> [!WARNING]
>
> **Container changes do not persist**
>
> Changes made outside your project directory are lost when the container restarts. Some Composer commands make such changes, see below. For other cases, see [Persisting Changes Across Restarts](<https://ddev.github.io/ddev/en/stable/users/extend/in-container-configuration/#persisting-changes-across-restarts>).

#### Composer Global Packages Do Not Persist

If you run `ddev composer global require` (or `composer global require` inside the web container), global packages will be installed at the home directory within the container (`$HOME/.composer`) and will disappear when the container restarts.

To keep them, [store `~/.composer` in `ddev-global-cache`](<https://ddev.github.io/ddev/en/stable/users/extend/in-container-configuration/#persisting-home-subdirectories-in-ddev-global-cache>), or install the tools with a [custom Dockerfile](<https://ddev.github.io/ddev/en/stable/users/extend/customizing-images/#examples>).

#### Composer Self-Update Changes Do Not Persist

Running `ddev composer self-update` (or `composer self-update` inside the web container) will update Composer within the container, but the change will be lost when the container restarts. Don’t try to keep `/usr/local/bin/composer` across restarts, because then `composer_version` and DDEV upgrades can’t update it.

The Composer version is cached after the first image build and automatically updates only when DDEV is upgraded.

If you need to update Composer:

1. Check the [`composer_version`](<https://ddev.github.io/ddev/en/stable/users/configuration/config/#composer_version>) setting in `.ddev/config.yaml` and set it to your desired version:

   - `composer_version: ""` (empty) - equivalent to `composer self-update --stable`
   - `composer_version: "2"` (v2) - equivalent to `composer self-update --2`
   - `composer_version: "2.2"` (v2.2 LTS) - equivalent to `composer self-update --2.2`
   - `composer_version: "2.9.3"` (specific) - equivalent to `composer self-update 2.9.3`. This is the most consistent option for teams, as it ensures all members use the exact same version regardless of when they first build the image, avoiding inconsistencies in dependency resolution.
2. [Restart](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#restart>) to install it:

   ```
   ddev restart
   ```

   To get a newer release without changing the value, for example with `""` or `"2"`, restart with `--no-cache` or [rebuild](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#utility-rebuild>). This rebuilds the whole image and takes longer:

   ```
   ddev restart --no-cache
   # or
   ddev utility rebuild
   ```

### Composer from `vendor/bin/composer`

If your `composer.json` specifies `composer/composer`, the version in `vendor/bin/composer` can be run with:

```
ddev exec vendor/bin/composer --version
```

If a custom [`composer_root`](<https://ddev.github.io/ddev/en/stable/users/configuration/config/#composer_root>) is set, use:

```
ddev exec '$DDEV_COMPOSER_ROOT/vendor/bin/composer --version'
```

### Windows OS (Traditional Windows) and `ddev composer`

DDEV attempts to help with Composer and some configurations of Docker Desktop for Windows that introduce complex filesystem workarounds.

Use [`ddev composer`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#composer>) (Composer inside the container) instead of using `composer` on the host side, because it uses the right version of PHP and all its extensions for your project:

- On some older configurations of Docker Desktop for Windows, symlinks are created in the container as “simulated symlinks”, or XSym files. These special text files behave as symlinks inside the container (on CIFS filesystem), but appear as simple text files on the Windows host. (On the CIFS filesystem used by Docker for Windows, inside the container, there is no capability to create real symlinks even though Windows now has this capability.)
- DDEV attempts to clean up for this situation. Since Windows 10/11+ (in developer mode) can create real symlinks, DDEV scans your repository after a `ddev composer` command and attempts to convert XSym files into real symlinks. On older versions of Windows 10, it can only do this if your Windows 10 workstation is set to “Developer Mode”.
- To enable developer mode on Windows 10/11+, search for “developer” in settings: ![Finding developer mode](<https://ddev.github.io/ddev/en/stable/images/developer-mode-1.png>)

  ![Setting developer mode](<https://ddev.github.io/ddev/en/stable/images/developer-mode-2.png>)

## Email Capture and Review (Mailpit)

[Mailpit](<https://github.com/axllent/mailpit>) is a mail catcher that’s configured to capture and display emails sent by PHP in the development environment.

After your project is started, access the Mailpit web interface at `https://mysite.ddev.site:8026`, or run [`ddev mailpit`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#mailpit>) to launch it in your default browser.

Mailpit will **not** intercept emails if your application is configured to use SMTP or a third-party ESP integration.

For Drupal 9+, `settings.ddev.php` automatically overrides [Symfony Mailer](<https://www.drupal.org/project/symfony_mailer>) and [Symfony Mailer Lite](<https://www.drupal.org/project/symfony_mailer_lite>) transport configurations to use Mailpit; for other SMTP modules (like [SMTP](<https://www.drupal.org/project/smtp>)), update your application’s SMTP server configuration to use `localhost` and Mailpit’s port `1025`.

For Laravel projects, Mailpit will capture Swift messages via SMTP. Update your `.env` to use Mailpit with the following settings:

```
MAIL_MAILER=smtp
MAIL_HOST=localhost
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
```

> [!NOTE]
>
> **Mailpit auto-tagging behavior**
>
> Mailpit automatically extracts [tags](<https://mailpit.axllent.org/docs/usage/tagging/#disable-auto-tagging>) from email addresses using *plus addressing*. For example, an email sent to `address+1@example.com` will be tagged with `1` (the part after `+`). This can be useful for filtering, but may be unexpected if you’re not aware of it.
>
> If this behavior is distracting, you can disable it by setting the following [environment variable](<https://ddev.github.io/ddev/en/stable/users/configuration/environment-variables/index.md>):
>
> ```
> MP_TAGS_DISABLE=plus-addresses
> ```
>
> The easiest way is to set it globally for all DDEV projects:
>
> ```
> ddev config global --web-environment-add=MP_TAGS_DISABLE=plus-addresses
> ```

## Using Development Tools on the Host Machine

It’s possible in many cases to use development tools installed on your host machine on a project provisioned by DDEV. Tools that interact with files and require no database connection, such as Git or Composer, can be run from the host machine against the codebase for a DDEV project with no additional configuration necessary.

### Database Connections from the Host

If you need to connect to your project’s database from your workstation, run [`ddev describe`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#describe>) to show the database connection information, like `Host: localhost:49156`.

Each project’s database port is unique, and randomly chosen from available ports on your system when you run [`ddev start`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#start>).

You can force this port to be the same on every [`ddev start`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#start>) by setting [`host_db_port`](<https://ddev.github.io/ddev/en/stable/users/configuration/config/#host_db_port>) in the project’s `.ddev/config.yaml`. For example, `host_db_port: "49156"` or `ddev config --host-db-port=49156`. This value needs to be different on each running DDEV project, and unless it is set, the database port will change on every `ddev start`.

You can use this port with various tools that need a direct port, like `mysql` or `psql` clients, but it’s usually easiest to use [`ddev mysql`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#mysql>), `ddev psql`, `ddev 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>), etc, which set everything up for you.

(If you use PhpStorm and its integrated database browser, use the [DDEV Integration Plugin](<https://plugins.jetbrains.com/plugin/18813-ddev-integration>) to manage all of this for you.)

## DDEV and Terminus

[Terminus](<https://docs.pantheon.io/terminus>) is a command-line tool providing advanced interaction with [Pantheon](<https://pantheon.io/>) services. `terminus` is available inside the project’s container, allowing users to get information from, or issue commands to their Pantheon-hosted sites. This is an especially helpful feature for Windows users since Terminus is only officially supported on Unix-based systems.

> [!TIP]
>
> **How to change Terminus version?**
>
> See [How to Downgrade Terminus in DDEV’s Web Container and Customize Other Bundled Tools](<https://ddev.com/blog/ddev-bundled-tools-using-custom-versions/>).

To use Terminus, you’ll first need to:

1. Use a machine token. (See [Pantheon provider discussion](<https://ddev.github.io/ddev/en/stable/users/providers/pantheon/index.md>).)
2. Run [`ddev ssh`](<https://ddev.github.io/ddev/en/stable/users/usage/commands/#ssh>) to tunnel into your container.
3. Issue a command using the keyword `terminus`. For help using Terminus, try `terminus list` to get a list of possible commands.

Terminus also allows you to issue [Drush](<https://www.drush.org/>), [WP-CLI](<https://wp-cli.org/>), and [Composer](<https://getcomposer.org/>) commands to your Pantheon server. These are all usable from within the container as well, but will require authentication via either your Pantheon password or an SSH key. To use your host machine’s SSH key, you can use the `ddev auth ssh` command [described here](<https://ddev.github.io/ddev/en/stable/users/usage/cli/#ssh-into-containers>).
