github.com/drud/ddev@v1.21.5-alpha1.0.20230226034409-94fcc4b94453/docs/content/users/extend/customizing-images.md (about) 1 # Customizing Docker Images 2 3 It’s common to have a requirement for the `web` or `db` images which isn’t bundled with them by default. There are two ways to extend these Docker images: 4 5 1. `webimage_extra_packages` and `dbimage_extra_packages` in `.ddev/config.yaml`. 6 2. An add-on Dockerfile in your project’s `.ddev/web-build` or `.ddev/db-build`. 7 8 ## Adding Extra Debian Packages with `webimage_extra_packages` and `dbimage_extra_packages` 9 10 You can add extra Debian packages with lines like this in `.ddev/config.yaml`: 11 12 ```yaml 13 webimage_extra_packages: [php-yaml, php8.2-tidy] 14 dbimage_extra_packages: [telnet, netcat] 15 ``` 16 17 Then the additional packages will be built into the containers during [`ddev start`](../usage/commands.md#start). 18 19 ## Determining What Packages You Need 20 21 The `web` container is a Debian image, and its PHP distributions are packaged (thank you!) by [`deb.sury.org`](https://deb.sury.org/). 22 23 Most PHP extensions are built in the `deb.sury.org` distribution. You can Google the extension you want, or download and search the [Packages](https://packages.sury.org/php/dists/buster/main/binary-amd64/Packages) list from the `sury` distribution. For example, the `bcmath` PHP extension is provided by `php-bcmath`. Many packages have version-specific names, like `php7.3-tidy`. 24 25 If you need a package that is *not* a PHP package, you can view and search standard Debian packages at [packages.debian.org/stable](https://packages.debian.org/stable/), or just use Google. 26 27 To test that a package will do what you want, you can [`ddev ssh`](../usage/commands.md#ssh) and `sudo apt-get update && sudo apt-get install <package>` to verify that you can install it and you get what you need. A PHP extension may require `killall -USR2 php-fpm` to take effect. After you’ve tried that, you can add the package to [`webimage_extra_packages`](../configuration/config.md#webimage_extra_packages). 28 29 ## Adding Extra Dockerfiles for `webimage` and `dbimage` 30 31 For more complex requirements, you can add: 32 33 * `.ddev/web-build/Dockerfile` 34 * `.ddev/web-build/Dockerfile.*` 35 * `.ddev/db-build/Dockerfile` 36 * `.ddev/db-build/Dockerfile.*` 37 38 These files’ content will be inserted into the constructed Dockerfile for each image. They are inserted *after* most of the rest of the things that are done to build the image, and are done in alphabetical order, so `Dockerfile` is inserted first, followed by `Dockerfile.*` in alphabetical order. 39 40 For certain use cases, you might need to add directives very early on the Dockerfile like proxy settings or SSL termination. You can use `pre.` variants for this that are inserted *before* everything else: 41 42 * `.ddev/web-build/pre.Dockerfile.*` 43 * `.ddev/db-build/pre.Dockerfile.*` 44 45 Examine the resultant generated Dockerfile (which you will never edit directly), at `.ddev/.webimageBuild/Dockerfile`. You can force a rebuild with [`ddev debug refresh`](../usage/commands.md#debug-refresh). 46 47 Examples of possible Dockerfiles are `.ddev/web-build/Dockerfile.example` and `.ddev/db-build/Dockerfile.example`, created in your project when you run [`ddev config`](../usage/commands.md#config). 48 49 You can use the `.ddev/*-build` directory as the Docker “context” directory as well. So for example, if a file named `README.txt` exists in `.ddev/web-build`, you can use `ADD README.txt /` in the Dockerfile. 50 51 An example web image `.ddev/web-build/Dockerfile` might be: 52 53 ```dockerfile 54 RUN npm install -g gatsby-cli 55 ``` 56 57 Another example would be installing `phpcs` globally (see [Stack Overflow answer](https://stackoverflow.com/questions/61870801/add-global-phpcs-and-drupal-coder-to-ddev-in-custom-dockerfile/61870802#61870802)): 58 59 ```dockerfile 60 ENV COMPOSER_HOME=/usr/local/composer 61 62 # We try to avoid relying on Composer to download global, so in `phpcs` case we can use the PHAR. 63 RUN curl -L https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar -o /usr/local/bin/phpcs && chmod +x /usr/local/bin/phpcs 64 RUN curl -L https://squizlabs.github.io/PHP_CodeSniffer/phpcbf.phar -o /usr/local/bin/phpcbf && chmod +x /usr/local/bin/phpcbf 65 66 # If however we need to download a package, we use `cgr` for that. 67 RUN composer global require consolidation/cgr 68 RUN $COMPOSER_HOME/vendor/bin/cgr drupal/coder:^8.3.1 69 RUN $COMPOSER_HOME/vendor/bin/cgr dealerdirect/phpcodesniffer-composer-installer 70 71 # Register Drupal’s code sniffer rules. 72 RUN phpcs --config-set installed_paths $COMPOSER_HOME/global/drupal/coder/vendor/drupal/coder/coder_sniffer --verbose 73 # Make Codesniffer config file writable for ordinary users in container. 74 RUN chmod 666 /usr/local/bin/CodeSniffer.conf 75 # Make `COMPOSER_HOME` writable if regular users need to use it. 76 RUN chmod -R ugo+rw $COMPOSER_HOME 77 # Now turn it off, because ordinary users will want to be using the default. 78 ENV COMPOSER_HOME="" 79 ``` 80 81 **Remember that the Dockerfile is normally building a Docker image that will be used later with DDEV.** At the time the Dockerfile is executing, your code by default is not mounted and the container is not running, it’s just being built. So for example, an `npm install` in `/var/www/html` will not do anything useful because the code is not there at image building time. However, you use `RUN` in the context of your codebase using something like `RUN --mount=type=bind,source=.,target=/var/www/html cp /var/www/html/index.php /var/tmp/` would mount your code and make it available at the normal `/var/www/html` mount. However, you can't make changes to the mounted code, so things like an early `npm install` wouldn't work, although `npm install -g` would work. 82 83 ### Build Time Environment Variables 84 85 The following environment variables are available for the web Dockerfile to use at build time: 86 87 * `$BASE_IMAGE`: the base image, like `drud/ddev-webserver:v1.21.4` 88 * `$username`: the username inferred from your host-side username 89 * `$uid`: the user ID inferred from your host-side user ID 90 * `$gid`: the group ID inferred from your host-side group ID 91 * `$DDEV_PHP_VERSION`: the PHP version declared in your project configuration (provided in versions after v1.21.4) 92 93 For example, a Dockerfile might want to build an extension for the configured PHP version like this: 94 95 ```Dockerfile 96 ENV extension=xhprof 97 ENV extension_repo=https://github.com/longxinH/xhprof 98 ENV extension_version=v2.3.8 99 # For versions <= DDEV v1.21.4 you must also declare DDEV_PHP_VERSION yourself: ENV DDEV_PHP_VERSION=8.1 100 101 RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -o Dpkg::Options::="--force-confnew" --no-install-recommends --no-install-suggests autoconf build-essential libc-dev php-pear php${DDEV_PHP_VERSION}-dev pkg-config zlib1g-dev 102 RUN mkdir -p /tmp/php-${extension} && cd /tmp/php-${extension} && git clone ${extension_repo} . 103 WORKDIR /tmp/php-${extension}/extension 104 RUN git checkout ${extension_version} 105 RUN phpize 106 RUN ./configure 107 RUN make install 108 RUN echo "extension=${extension}.so" > /etc/php/${DDEV_PHP_VERSION}/mods-available/${extension}.ini 109 ``` 110 111 ### Debugging the Dockerfile Build 112 113 It can be complicated to figure out what’s going on when building a Dockerfile, and even more complicated when you’re seeing it go by as part of [`ddev start`](../usage/commands.md#start). 114 115 1. Use [`ddev ssh`](../usage/commands.md#ssh) first of all to pioneer the steps you want to take. You can do all the things you need to do there and see if it works. If you’re doing something that affects PHP, you may need to `sudo killall -USR2 php-fpm` for it to take effect. 116 2. Put the steps you pioneered into `.ddev/web-build/Dockerfile` as above. 117 3. If you can’t figure out what’s failing or why, then `~/.ddev/bin/docker-compose -f .ddev/.ddev-docker-compose-full.yaml build web --no-cache --progress=plain` to see what’s happening during the Dockerfile build.