github.com/ddev/ddev@v1.23.2-0.20240519125000-d824ffe36ff3/docs/content/users/extend/customization-extendibility.md (about)

     1  ---
     2  search:
     3    boost: 2
     4  ---
     5  
     6  # Extending and Customizing Environments
     7  
     8  DDEV provides several ways to customize and extend project environments.
     9  
    10  ## Changing PHP Version
    11  
    12  The project's `.ddev/config.yaml` file defines the PHP version to use. The [`php_version`](../configuration/config.md#php_version) can be changed to `5.6`, `7.0`, `7.1`, `7.2`,  `7.3`, `7.4`, `8.0`, `8.1`, `8.2`, or `8.3`.
    13  
    14  ### Older Versions of PHP
    15  
    16  [Support for older versions of PHP is available on ddev-contrib](https://github.com/ddev/ddev-contrib/blob/master/docker-compose-services/old_php) via [custom docker-compose files](custom-compose-files.md).
    17  
    18  ## Changing Web Server Type
    19  
    20  DDEV supports nginx with php-fpm by default (`nginx-fpm`), and Apache with php-fpm (`apache-fpm`). You can change this with the [`webserver_type`](../configuration/config.md#webserver_type) config option, or using the [`ddev config`](../usage/commands.md#config) command with the `--webserver-type` flag.
    21  
    22  ## Adding Services to a Project
    23  
    24  DDEV provides everything you need to build a modern PHP application on your local machine. More complex web applications, however, often require integration with services beyond the usual requirements of a web and database server—maybe Apache Solr, Redis, Varnish, or many others. While DDEV likely won’t ever provide all of these additional services out of the box, it’s designed to provide simple ways to customize the environment and meet your project’s needs without reinventing the wheel.
    25  
    26  A collection of vetted service configurations is available in the [Additional Services Documentation](additional-services.md).
    27  
    28  If you need to create a service configuration for your project, see [Defining Additional Services with Docker Compose](custom-compose-files.md).
    29  
    30  ## Using Node.js with DDEV
    31  
    32  There are many ways to deploy Node.js in any project, so DDEV tries to let you set up any possibility you can come up with.
    33  
    34  * You can choose any Node.js version you want (including minor and older versions) in `.ddev/config.yaml` with [`nodejs_version`](../configuration/config.md#nodejs_version).
    35  * [`ddev nvm`](../usage/commands.md#nvm) gives you the full capabilities of [Node Version Manager](https://github.com/nvm-sh/nvm).
    36  * [`ddev npm`](../usage/commands.md#npm) and [`ddev yarn`](../usage/commands.md#yarn) provide shortcuts to the `npm` and `yarn` commands inside the container, and their caches are persistent.
    37  * You can run Node.js daemons using [`web_extra_daemons`](#running-extra-daemons-in-the-web-container).
    38  * You can expose Node.js ports via `ddev-router` by using [`web_extra_exposed_ports`](#exposing-extra-ports-via-ddev-router).
    39  * You can manually run Node.js scripts using [`ddev exec <script>`](../usage/commands.md#exec) or `ddev exec node <script>`.
    40  
    41  !!!tip "Please share your techniques!"
    42      There are several ways to share your favorite Node.js tips and techniques. Best are [ddev-get add-ons](additional-services.md), [Stack Overflow](https://stackoverflow.com/tags/ddev), and [ddev-contrib](https://github.com/ddev/ddev-contrib).
    43  
    44  ## Running Extra Daemons in the Web Container
    45  
    46  There are several ways to run processes inside the `web` container.
    47  
    48  1. Manually execute them as needed, with [`ddev exec`](../usage/commands.md#exec), for example.
    49  2. Run them with a `post-start` [hook](../configuration/hooks.md).
    50  3. Run them automatically using `web_extra_daemons`.
    51  
    52  !!!tip "Daemons requiring network access should bind to `0.0.0.0`, not to `localhost` or `127.0.0.1`"
    53      Many examples on the internet show starting daemons starting up and binding to `127.0.0.1` or `localhost`. Those examples are assuming that network consumers are on the same network interface, but with a DDEV-based solution the network server is essentially on a different computer from the host computer (workstation). If the host computer needs to have connectivity, then bind to `0.0.0.0` (meaning "all network interfaces") rather than `127.0.0.1` or `localhost` (which means only allow access from the local network). A [ReactPHP example](https://github.com/orgs/ddev/discussions/5973) would be:
    54      <!-- markdownlint-disable -->
    55      ```php
    56      $socket = new React\Socket\SocketServer('0.0.0.0:3000');
    57      ```
    58      <!-- markdownlint-restore -->
    59      instead of:
    60      <!-- markdownlint-disable -->
    61      ```php
    62      $socket = new React\Socket\SocketServer('127.0.0.1:3000');
    63      ```
    64      <!-- markdownlint-restore -->
    65      To expose your daemon to the workstation and browser, see [Exposing Extra Ports via `ddev-router`](#exposing-extra-ports-via-ddev-router).
    66  
    67  ### Running Extra Daemons with a `post-start` Hook
    68  
    69  Daemons can be run with a `post-start` `exec` hook or automatically started using `supervisord`.
    70  
    71  A simple `post-start` exec hook in `.ddev/config.yaml` might look like:
    72  
    73  ```yaml
    74  hooks:
    75    post-start:
    76      - exec: "nohup php --docroot=/var/www/html/something -S 0.0.0.0:6666 &"
    77  ```
    78  
    79  ### Running Extra Daemons Using `web_extra_daemons`
    80  
    81  If you need extra daemons to start up automatically inside the web container, you can easily add them using [`web_extra_daemons`](../configuration/config.md#web_extra_daemons) in `.ddev/config.yaml`.
    82  
    83  You might be running Node.js daemons that serve a particular purpose, like `browsersync`, or more general daemons like a `cron` daemon.
    84  
    85  For example, you could use this configuration to run two instances of the Node.js HTTP server for different directories:
    86  
    87  ```yaml
    88  web_extra_daemons:
    89    - name: "http-1"
    90      command: "/var/www/html/node_modules/.bin/http-server -p 3000"
    91      directory: /var/www/html
    92    - name: "http-2"
    93      command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000"
    94      directory: /var/www/html
    95  ```
    96  
    97  !!!tip "How to view the results of a daemon start attempt?"
    98      See [`ddev logs`](../usage/commands.md#logs) or `docker logs ddev-<project>-web`.
    99  
   100  * `directory` should be the absolute path inside the container to the directory where the daemon should run.
   101  * `command` is best as a simple binary with its arguments, but Bash features like `cd` or `&&` work. If the program to be run is not in the `ddev-webserver` `$PATH` then it should have the absolute in-container path to the program to be run, like `/var/www/html/node_modules/.bin/http-server`.
   102  * `web_extra_daemons` is a shortcut for adding a configuration to `supervisord`, which organizes daemons inside the web container. If the default settings are inadequate for your use, you can write a [complete config file for your daemon](#explicit-supervisord-configuration-for-additional-daemons).
   103  * Your daemon is expected to run in the foreground, not to daemonize itself, `supervisord` will take care of that.
   104  
   105  ## Exposing Extra Ports via `ddev-router`
   106  
   107  If your `web` container has additional HTTP servers running inside it on different ports, those can be exposed using [`web_extra_exposed_ports`](../configuration/config.md#web_extra_exposed_ports) in `.ddev/config.yaml`. For example, this configuration would expose a `node-vite` HTTP server running on port 3000 inside the `web` container, via `ddev-router`, to ports 9998 (HTTP) and 9999 (HTTPS), so it could be accessed via `https://<project>.ddev.site:9999`:
   108  
   109  ```yaml
   110  web_extra_exposed_ports:
   111    - name: node-vite
   112      container_port: 3000
   113      http_port: 9998
   114      https_port: 9999
   115  ```
   116  
   117  The configuration below would expose a Node.js server running in the `web` container on port 3000 as `https://<project>.ddev.site:3000` and a “something” server running in the web container on port 4000 as `https://<project>.ddev.site:4000`:
   118  
   119  ```yaml
   120  web_extra_exposed_ports:
   121    - name: nodejs
   122      container_port: 3000
   123      http_port: 2999
   124      https_port: 3000
   125    - name: something
   126      container_port: 4000
   127      https_port: 4000
   128      http_port: 3999
   129  ```
   130  
   131  !!!warning "Fill in all three fields even if you don’t intend to use the `https_port`!"
   132      If you don’t add `https_port`, then it defaults to `0` and `ddev-router` will fail to start.
   133  
   134  ## Exposing Extra Non-HTTP Ports
   135  
   136  While the `web_extra_exposed_ports` gracefully handles running multiple DDEV projects at the same time, it can't forward ports for non-HTTP TCP or UDP daemons. Instead, ports can be added in a `docker-compose.*.yaml` file. This file does not need to specify an additional services. For example, this configuration exposes port 5900 for a VNC server.
   137  
   138  In `.ddev/docker-compose.vnc.yaml`:
   139  
   140  ```yaml
   141  services:
   142    web:
   143      ports:
   144        - "5900:5900"
   145  ```
   146  
   147  If multiple projects declare the same port, only the first project will be able to start successfully. Consider making services like this disabled by default, especially if they aren't needed in day to day use.
   148  
   149  ## Providing Custom Environment Variables to a Container
   150  
   151  You can set custom environment variables in several places:
   152  
   153  * The project’s [`web_environment`](../configuration/config.md#web_environment) setting in `.ddev/config.yaml` or `.ddev/config.*.yaml`:
   154  
   155      ```yaml
   156      web_environment:
   157      - MY_ENV_VAR=someval
   158      - MY_OTHER_ENV_VAR=someotherval
   159      ```
   160  
   161  * The global `web_environment` setting in `.ddev/global_config.yaml`.
   162  
   163  * An optional, project-level `.ddev/.env` file, which could look something like this:
   164  
   165      ```
   166      MY_ENV_VAR='someval'
   167      MY_OTHER_ENV_VAR='someotherval'
   168      ```
   169  
   170  If you’d rather use the CLI to set the project or global `web_environment` value, you can use the [`ddev config`](../usage/commands.md#config) command:
   171  
   172  ```sh
   173  # Set MY_ENV_VAR for the project
   174  ddev config --web-environment-add="MY_ENV_VAR=someval"
   175  
   176  # Set MY_ENV_VAR globally
   177  ddev config global --web-environment-add="MY_ENV_VAR=someval
   178  ```
   179  
   180  You can use the `--web-environment` flag to overwrite existing values rather than adding them.
   181  
   182  !!!warning "Don’t check in sensitive values!"
   183      Sensitive variables like API keys should not be checked in with your project. Typically you might use an `.env` file and _not_ check that in, but offer `.env.example` with expected keys that don’t have values. Some use global configuration for sensitive values, as that’s not normally checked in either.
   184  
   185  ### Altering the In-Container `$PATH`
   186  
   187  Sometimes it’s easiest to put the command you need into the existing `$PATH` using a symbolic link rather than changing the in-container `$PATH`. For example, the project `bin` directory is already included the `$PATH`. So if you have a command you want to run that’s not already in the `$PATH`, you can add a symlink.
   188  
   189  Examples:
   190  
   191  * On Craft CMS, the `craft` script is often in the project root, which is not in the `$PATH`. But if you `mkdir bin && ln -s craft bin/craft` you should be able to run `ddev exec craft`. (Note however that `ddev craft` takes care of this for you.)
   192  * On projects where the `vendor` directory is not in the project root (Acquia projects, for example, have `composer.json` and `vendor` in the `docroot` directory), you can `mkdir bin && ln -s docroot/vendor/bin/drush bin/drush` to put `drush` in your `$PATH`. (With projects like this, make sure to set `composer_root: docroot` so that `ddev composer` works properly.)
   193  
   194  You can also modify the `PATH` environment variable by adding a script to `<project>/.ddev/homeadditions/.bashrc.d/` or (global) `~/.ddev/homeadditions/.bashrc.d/`. For example, if your project vendor directory is not in the expected place (`/var/www/html/vendor/bin`) you can add a `<project>/.ddev/homeadditions/.bashrc.d/path.sh`:
   195  
   196  ```bash
   197  export PATH=$PATH:/var/www/html/somewhereelse/vendor/bin
   198  ```
   199  
   200  ## Custom nginx Configuration
   201  
   202  When you run [`ddev restart`](../usage/commands.md#restart) using `nginx-fpm`, DDEV creates a configuration customized to your project type in `.ddev/nginx_full/nginx-site.conf`. You can edit and override the configuration by removing the `#ddev-generated` line and doing whatever you need with it. After each change, run `ddev restart`. (For updates without restart, see [Troubleshooting nginx Configuration](#troubleshooting-nginx-configuration).)
   203  
   204  You can also have more than one config file in the `.ddev/nginx_full` directory, and each will be loaded when DDEV starts. This can be used for [serving multiple docroots](#multiple-docroots-in-nginx-advanced) and other techniques.
   205  
   206  ### Troubleshooting nginx Configuration
   207  
   208  * Any errors in your configuration may cause the `web` container to fail and try to restart. If you see that behavior, use [`ddev logs`](../usage/commands.md#logs) to diagnose.
   209  * The configuration is copied into the container during restart. Therefore it is not possible to edit the host file for the changes to take effect. You may want to edit the file directly inside the container at `/etc/nginx/sites-enabled/`. (For example, run [`ddev ssh`](../usage/commands.md#ssh) to get into the container.)
   210  * You can run `ddev exec nginx -t` to test whether your configuration inside the container is valid. (Or run [`ddev ssh`](../usage/commands.md#ssh) and run `nginx -t`.)
   211  * You can reload the nginx configuration by running either [`ddev restart`](../usage/commands.md#restart) or editing the configuration inside the container at `/etc/nginx/sites-enabled/` and running `ddev exec nginx -s reload` on the host system (inside the container run `nginx -s reload`).
   212  * The alias `Alias "/phpstatus" "/var/www/phpstatus.php"` is required for the health check script to work.
   213  
   214  ### Multiple Docroots in nginx (Advanced)
   215  
   216  It’s easiest to have different web servers in different DDEV projects, and DDEV projects can [easily communicate with each other](../usage/faq.md), but some sites require more than one docroot for a single project codebase. Sometimes this is because there’s an API in the same codebase but using different code, or different code for different languages, etc.
   217  
   218  The generated `.ddev/nginx_full/seconddocroot.conf.example` demonstrates how to do this. You can create as many of these as you want: change the `servername` and the `root` and customize as needed.
   219  
   220  ### nginx Snippets
   221  
   222  To add an nginx snippet to the default config, add an nginx config file as `.ddev/nginx/<something>.conf`.
   223  
   224  For example, to make all HTTP URLs redirect to their HTTPS equivalents you might add `.ddev/nginx/redirect.conf` with this stanza:
   225  
   226  ```
   227      if ($http_x_forwarded_proto = "http") {
   228        return 301 https://$host$request_uri;
   229      }
   230  ```
   231  
   232  After adding a snippet, run `ddev restart` to make it take effect.
   233  
   234  ## Custom Apache Configuration
   235  
   236  If you’re using [`webserver_type: apache-fpm`](../configuration/config.md#webserver_type) in your `.ddev/config.yaml`, you can override the default site configuration by editing or replacing the DDEV-provided `.ddev/apache/apache-site.conf` configuration.
   237  
   238  When you run [`ddev restart`](../usage/commands.md#restart) using `apache-fpm`, DDEV creates a configuration customized to your project type in `.ddev/apache/apache-site.conf`. You can edit and override the configuration by removing the `#ddev-generated` line and doing whatever you need with it. After each change, run `ddev restart`.
   239  
   240  * Edit the `.ddev/apache/apache-site.conf`.
   241  * Remove the `#ddev-generated` to signal to DDEV that you're taking control of the file.
   242  * Add your configuration changes.
   243  * Save your configuration file and run [`ddev restart`](../usage/commands.md#restart). If you encounter issues with your configuration or the project fails to start, use [`ddev logs`](../usage/commands.md#logs) to inspect the logs for possible Apache configuration errors.
   244  * Use `ddev exec apachectl -t` to do a general Apache syntax check.
   245  * The alias `Alias "/phpstatus" "/var/www/phpstatus.php"` is required for the health check script to work.
   246  * Any errors in your configuration may cause the `web` container to fail. If you see that behavior, use `ddev logs` to diagnose.
   247  
   248  !!!warning "Important!"
   249      Changes to `.ddev/apache/apache-site.conf` take place on a [`ddev restart`](../usage/commands.md#restart). You can also `ddev exec apachectl -k graceful` to reload the Apache configuration.
   250  
   251  ## Custom PHP Configuration (`php.ini`)
   252  
   253  You can provide additional PHP configuration for a project by creating a directory called `.ddev/php/` and adding any number of `*.ini` PHP configuration files.
   254  
   255  You should generally limit your override to any specific option(s) you need to customize. Every file in `.ddev/php/` will be copied into `/etc/php/[version]/(cli|fpm)/conf.d`, so it’s possible to replace files that already exist in the container. Common usage is to put custom overrides in a file called `my-php.ini`. Make sure you include the section header that goes with each item (like `[PHP]`).
   256  
   257  One interesting implication of this behavior is that it’s possible to disable extensions by replacing the configuration file that loads them. For instance, if you were to create an empty file at `.ddev/php/20-xdebug.ini`, it would replace the configuration that loads Xdebug, which would cause Xdebug to not be loaded!
   258  
   259  To load the new configuration, run [`ddev restart`](../usage/commands.md#restart).
   260  
   261  An example file in `.ddev/php/my-php.ini` might look like this:
   262  
   263  ```ini
   264  [PHP]
   265  max_execution_time = 240;
   266  ```
   267  
   268  ## Custom MySQL/MariaDB configuration (`my.cnf`)
   269  
   270  You can provide additional MySQL/MariaDB configuration for a project by creating a directory called `.ddev/mysql/` and adding any number of `*.cnf` MySQL configuration files. These files will be automatically included when MySQL is started. Make sure that the section header is included in the file.
   271  
   272  An example file in `.ddev/mysql/no_utf8mb4.cnf` might be:
   273  
   274  ```
   275  [mysqld]
   276  collation-server = utf8_general_ci
   277  character-set-server = utf8
   278  innodb_large_prefix=false
   279  ```
   280  
   281  To load the new configuration, run [`ddev restart`](../usage/commands.md#restart).
   282  
   283  ## Custom PostgreSQL Configuration
   284  
   285  If you’re using PostgreSQL, a default `posgresql.conf` is provided in `.ddev/postgres/postgresql.conf`. If you need to alter it, remove the `#ddev-generated` line and [`ddev restart`](../usage/commands.md#restart).
   286  
   287  ## Extending `config.yaml` with Custom `config.*.yaml` Files
   288  
   289  You may add additional `config.*.yaml` files to organize additional commands as you see fit for your project and team.
   290  
   291  For example, many teams commit their `config.yaml` and share it throughout the team, but some team members may require overrides to the checked-in version specifically for their environment and not checked in. For example, a team member may want to use a [`router_http_port`](../configuration/config.md#router_http_port) other than the team default due to a conflict in their development environment. In this case they could add `.ddev/config.ports.yaml`:
   292  
   293  ```yaml
   294  # My machine can’t use port 80 so override with port 8080, but don’t check this in!
   295  router_http_port: 8080
   296  ```
   297  
   298  Extra `config.*.yaml` files are loaded in lexicographic order, so `config.a.yaml` will be overridden by `config.b.yaml`.
   299  
   300  Team members may choose to use `config.local.yaml` for local non-committed config changes, for example. `config.local.yaml` is gitignored by default.
   301  
   302  `config.*.yaml` update configuration according to these rules:
   303  
   304  1. Simple fields like [`router_http_port`](../configuration/config.md#router_http_port) or [`webserver_type`](../configuration/config.md#webserver_type) are overwritten.
   305  2. Lists of strings like [`additional_hostnames`](../configuration/config.md#additional_hostnames) or [`additional_fqdns`](../configuration/config.md#additional_fqdns) are merged.
   306  3. The list of environment variables in [`web_environment`](../configuration/config.md#web_environment) are “smart merged”: if you add the same environment variable with a different value, the value in the override file will replace the value from `config.yaml`.
   307  4. Hook specifications in the [`hooks`](../configuration/config.md#hooks) variable are merged.
   308  
   309  If you need to _override_ existing values, set [`override_config: true`](../configuration/config.md#override_config) in the `config.*.yaml` where the override behavior should take place. Since `config.*.yaml` files are normally _merged_ into the configuration, some things can’t be overridden normally. For example, if you have [`use_dns_when_possible: false`](../configuration/config.md#use_dns_when_possible) you can’t override it with a merge and you can’t erase existing hooks or all environment variables. However, with `override_config: true` in a particular `config.*.yaml` file,
   310  
   311  ```yaml
   312  override_config: true
   313  use_dns_when_possible: false
   314  ```
   315  
   316  can override the existing values, and
   317  
   318  ```yaml
   319  override_config: true
   320  hooks:
   321    post-start: []
   322  ```
   323  
   324  or
   325  
   326  ```yaml
   327  override_config: true
   328  additional_hostnames: []
   329  ```
   330  
   331  can have their intended affect.
   332  
   333  [`override_config`](../configuration/config.md#override_config) affects only behavior of the `config.*.yaml` file it exists in.
   334  
   335  To experiment with the behavior of a set of `config.*.yaml` files, use the [`ddev debug configyaml`](../usage/commands.md#debug-configyaml) file; it’s especially valuable with the `yq` command, for example `ddev debug configyaml | yq`.
   336  
   337  ## Explicit `supervisord` Configuration for Additional Daemons
   338  
   339  Although most extra daemons (like Node.js daemons, etc.) can be configured easily using [web_extra_daemons](#running-extra-daemons-in-the-web-container), there may be situations where you want complete control of the `supervisord` configuration.
   340  
   341  In these case you can create a `.ddev/web-build/<daemonname>.conf` with configuration like:
   342  
   343  ```
   344  [program:daemonname]
   345  command=/var/www/html/path/to/daemon
   346  directory=/var/www/html/
   347  autorestart=true
   348  startretries=3
   349  stdout_logfile=/var/tmp/logpipe
   350  stdout_logfile_maxbytes=0
   351  redirect_stderr=true
   352  ```
   353  
   354  And create a `.ddev/web-build/Dockerfile.<daemonname>` to install the config file:
   355  
   356  ```dockerfile
   357  ADD daemonname.conf /etc/supervisor/conf.d
   358  ```
   359  
   360  Full details for advanced configuration possibilities are in [Supervisor docs](http://supervisord.org/configuration.html#program-x-section-settings).