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