github.com/ddev/ddev@v1.23.2-0.20240519125000-d824ffe36ff3/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${DDEV_PHP_VERSION}-yaml", "php${DDEV_PHP_VERSION}-tidy"]
    14  dbimage_extra_packages: [telnet, netcat, sudo]
    15  ```
    16  
    17  Then the additional packages will be built into the containers during [`ddev start`](../usage/commands.md#start).
    18  
    19  ## Adding PHP Extensions
    20  
    21  ### PHP Extensions supported by `deb.sury.org`
    22  
    23  If a PHP extension is supported by the upstream package management from `deb.sury.org`, you'll be able to add it with minimal effort. Test to see if it's available using `ddev exec 'sudo apt update && sudo apt install php${DDEV_PHP_VERSION}-<extension>'`, for example, `ddev exec 'sudo apt update && sudo apt install php${DDEV_PHP_VERSION}-imap'`. If that works, then the extension is supported, and you can add `webimage_extra_packages: ["php${DDEV_PHP_VERSION}-<extension>"]` to your `.ddev/config.yaml` file.
    24  
    25  ### PECL PHP Extensions not supported by `deb.sury.org`
    26  
    27  !!!tip "Few people need pecl extensions"
    28      Most people don't need to install PHP extensions that aren't supported by `deb.sury.org`, so you only need to go down this path if you have very particular needs.
    29  
    30  If a PHP extension is not supported by the upstream package management from `deb.sury.org`, you'll install it via pecl using a `.ddev/web-build/Dockerfile`. You can search for the extension on [pecl.php.net](https://pecl.php.net/) to find the package name. (This technique can also be used to get newer versions of PHP extensions than are available in the `deb.sury.org` distribution.)
    31  
    32  For example, a `.ddev/web-build/Dockerfile.mcrypt` might look like this:
    33  
    34  ```dockerfile
    35  ENV extension=mcrypt
    36  SHELL ["/bin/bash", "-c"]
    37  # Install the needed development packages
    38  RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -o Dpkg::Options::="--force-confnew" --no-install-recommends --no-install-suggests build-essential php-pear php${DDEV_PHP_VERSION}-dev
    39  # mcrypt happens to require libmcrypt-dev
    40  RUN apt install -y libmcrypt-dev
    41  RUN pecl install ${extension}
    42  RUN echo "extension=${extension}.so" > /etc/php/${DDEV_PHP_VERSION}/mods-available/${extension}.ini && chmod 666 /etc/php/${DDEV_PHP_VERSION}/mods-available/${extension}.ini
    43  RUN phpenmod ${extension}
    44  ```
    45  
    46  A `.ddev/web-build/Dockerfile.xlswriter` to add `xlswriter` might be:
    47  
    48  ```dockerfile
    49  ENV extension=xlswriter
    50  SHELL ["/bin/bash", "-c"]
    51  # Install the needed development packages
    52  RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -o Dpkg::Options::="--force-confnew" --no-install-recommends --no-install-suggests build-essential php-pear php${DDEV_PHP_VERSION}-dev
    53  # xlswriter requires libz-dev
    54  RUN sudo apt install -y libz-dev
    55  RUN echo | pecl install ${extension}
    56  RUN echo "extension=${extension}.so" > /etc/php/${DDEV_PHP_VERSION}/mods-available/${extension}.ini && chmod 666 /etc/php/${DDEV_PHP_VERSION}/mods-available/${extension}.ini
    57  RUN phpenmod ${extension}
    58  
    59  ```
    60  
    61  A `.ddev/web-build/Dockerfile.xdebug` (overriding the `deb.sury.org` version) might look like this:
    62  
    63  ```dockerfile
    64  # This example installs xdebug from pecl instead of the standard deb.sury.org package
    65  ENV extension=xdebug
    66  SHELL ["/bin/bash", "-c"]
    67  RUN phpdismod xdebug
    68  # Install the needed development packages
    69  RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y -o Dpkg::Options::="--force-confnew" --no-install-recommends --no-install-suggests build-essential php-pear php${DDEV_PHP_VERSION}-dev
    70  # Remove the standard xdebug provided by deb.sury.org
    71  RUN apt remove php${DDEV_PHP_VERSION}-xdebug || true
    72  RUN pecl install ${extension}
    73  # Use the standard xdebug.ini from source
    74  ADD https://raw.githubusercontent.com/ddev/ddev/master/containers/ddev-php-base/ddev-php-files/etc/php/8.2/mods-available/xdebug.ini /etc/php/${DDEV_PHP_VERSION}/mods-available
    75  RUN chmod 666 /etc/php/${DDEV_PHP_VERSION}/mods-available/xdebug.ini
    76  # ddev xdebug handles enabling module so we don't enable here
    77  #RUN phpenmod ${extension}
    78  ```
    79  
    80  ## Determining What Packages You Need
    81  
    82  The `web` container is a Debian image, and its PHP distributions are packaged (thank you!) by [`deb.sury.org`](https://deb.sury.org/).
    83  
    84  Most PHP extensions are built within 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`.
    85  
    86  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 use Google.
    87  
    88  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).
    89  
    90  ## Adding Extra Dockerfiles for `webimage` and `dbimage`
    91  
    92  For more complex requirements, you can add:
    93  
    94  * `.ddev/web-build/Dockerfile`
    95  * `.ddev/web-build/Dockerfile.*`
    96  * `.ddev/db-build/Dockerfile`
    97  * `.ddev/db-build/Dockerfile.*`
    98  
    99  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.
   100  
   101  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:
   102  
   103  * `.ddev/web-build/pre.Dockerfile.*`
   104  * `.ddev/db-build/pre.Dockerfile.*`
   105  
   106  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).
   107  
   108  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).
   109  
   110  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.
   111  
   112  An example web image `.ddev/web-build/Dockerfile` might be:
   113  
   114  ```dockerfile
   115  RUN npm install -g gatsby-cli
   116  ```
   117  
   118  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)):
   119  
   120  ```dockerfile
   121  ENV COMPOSER_HOME=/usr/local/composer
   122  
   123  # We try to avoid relying on Composer to download global, so in `phpcs` case we can use the PHAR.
   124  RUN curl -L https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar -o /usr/local/bin/phpcs && chmod +x /usr/local/bin/phpcs
   125  RUN curl -L https://squizlabs.github.io/PHP_CodeSniffer/phpcbf.phar -o /usr/local/bin/phpcbf && chmod +x /usr/local/bin/phpcbf
   126  
   127  # If however we need to download a package, we use `cgr` for that.
   128  RUN composer global require consolidation/cgr
   129  RUN $COMPOSER_HOME/vendor/bin/cgr drupal/coder:^8.3.1
   130  RUN $COMPOSER_HOME/vendor/bin/cgr dealerdirect/phpcodesniffer-composer-installer
   131  
   132  # Register Drupal’s code sniffer rules.
   133  RUN phpcs --config-set installed_paths $COMPOSER_HOME/global/drupal/coder/vendor/drupal/coder/coder_sniffer --verbose
   134  # Make Codesniffer config file writable for ordinary users in container.
   135  RUN chmod 666 /usr/local/bin/CodeSniffer.conf
   136  # Make `COMPOSER_HOME` writable if regular users need to use it.
   137  RUN chmod -R ugo+rw $COMPOSER_HOME
   138  # Now turn it off, because ordinary users will want to be using the default.
   139  ENV COMPOSER_HOME=""
   140  ```
   141  
   142  **Remember that the Dockerfile is building a Docker image that will be used later with DDEV.** At the time the Dockerfile is executing, your code is not mounted and the container is not running, the image is being built. So for example, an `npm install` in `/var/www/html` will not do anything to your project because the code is not there at image building time.
   143  
   144  ### Build Time Environment Variables
   145  
   146  The following environment variables are available for the web Dockerfile to use at build time:
   147  
   148  * `$BASE_IMAGE`: the base image, like `ddev/ddev-webserver:v1.21.4`
   149  * `$username`: the username inferred from your host-side username
   150  * `$uid`: the user ID inferred from your host-side user ID
   151  * `$gid`: the group ID inferred from your host-side group ID
   152  * `$DDEV_PHP_VERSION`: the PHP version declared in your project configuration (provided in versions after v1.21.4)
   153  
   154  For example, a Dockerfile might want to build an extension for the configured PHP version like this:
   155  
   156  ```Dockerfile
   157  ENV extension=xhprof
   158  ENV extension_repo=https://github.com/longxinH/xhprof
   159  ENV extension_version=v2.3.8
   160  
   161  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
   162  RUN mkdir -p /tmp/php-${extension} && cd /tmp/php-${extension} && git clone ${extension_repo} .
   163  WORKDIR /tmp/php-${extension}/extension
   164  RUN git checkout ${extension_version}
   165  RUN phpize
   166  RUN ./configure
   167  RUN make install
   168  RUN echo "extension=${extension}.so" > /etc/php/${DDEV_PHP_VERSION}/mods-available/${extension}.ini
   169  ```
   170  
   171  ## Installing into the home directory
   172  
   173  The in-container home directory is rebuilt when you run `ddev restart`, so if you have something that installs into the home directory (like `~/.cache`) you'll want to switch users in the Dockerfile. In this example, `npx playwright install` installs a number of things into `~/.cache`, so we'll switch to the proper user before executing it, and switch back to the `root` user after installation to avoid surprises with any other Dockerfile that may follow.
   174  
   175  ```Dockerfile
   176  USER $username
   177  # This is an example of creating a file in the home directory
   178  RUN touch ~/${username}-was-here
   179  # `npx playwright` installs lots of things in ~/.cache
   180  RUN npx playwright install
   181  RUN npx playwright install-deps
   182  USER root
   183  ```
   184  
   185  ### Debugging the Dockerfile Build
   186  
   187  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).
   188  
   189  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.
   190  2. Put the steps you pioneered into `.ddev/web-build/Dockerfile` as above.
   191  3. If you can’t figure out what’s failing or why, running `ddev debug refresh` will show the full output of the build process. You can also run `export DDEV_VERBOSE=true && ddev start` to see what’s happening during the `ddev start` Dockerfile build.