From 9eaea7d89d0fa805b86acfcc3c74485f31758953 Mon Sep 17 00:00:00 2001 From: CyberSymbol Date: Fri, 4 Sep 2026 05:53:38 +0300 Subject: [PATCH 1/4] docs: make legacy MySQL 5.5 example restart-safe --- ...legacy-projects-with-unsupported-php-and-mysql-using-ddev.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md b/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md index fb1825ca7..283bc9c74 100644 --- a/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md +++ b/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md @@ -69,6 +69,7 @@ services: cp /docker-entrypoint.sh ~/docker-entrypoint.sh sed -i '157s|.*|if false; then|' ~/docker-entrypoint.sh sed -i '175s|.*|echo mysql_8.0 >/var/lib/mysql/db_mariadb_version.txt|' ~/docker-entrypoint.sh + sed -i '81i\ chmod -f -R u+w /etc/mysql/conf.d/* 2>/dev/null || true' ~/docker-entrypoint.sh exec ~/docker-entrypoint.sh ``` @@ -77,6 +78,7 @@ Three things are noteworthy: - Setting `linux/amd64` as the platform will require Rosetta to be available on the macOS ARM64 platform - The `BASE_IMAGE` is set to a DDEV `db` container of legacy Docker images that are still provided. - Changing the `entrypoint` is a workaround to prevent DDEV complaining about a mismatching MySQL version after restarting the project. The small script "tricks" the DDEV inspection into believing, the version matches the one configured in `.ddev/config.yaml`. +- The `mysql-5.5:v1.24.6` image removes write permission from copied custom `.cnf` files, which can cause `Permission denied` when restarting an existing DB container; the added `chmod` makes this example restart-safe. This underlying issue was fixed in DDEV v1.24.7. ## Step 3: Rewire PHP From 841cc9408ecd3fa35de004e90b94a3b4406579b7 Mon Sep 17 00:00:00 2001 From: Stanislav Zhuk Date: Sat, 5 Sep 2026 14:35:03 +0300 Subject: [PATCH 2/4] docs: update legacy PHP/MySQL guide for current DDEV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## The Issue - Fixes #737 The legacy MySQL 5.5 guide pinned `ddev/ddev-dbserver-mysql-5.5:v1.24.6`, which can fail on restart with `Permission denied` when a custom `.ddev/mysql/*.cnf` is present. The pin also left the rest of the recipe stale: the entrypoint was patched by line number, so it only worked with that one image. ## How This PR Solves The Issue A `pre-start` hook derives the legacy image from `$DDEV_DBIMAGE`, so it always matches the installed DDEV version. That fixes the restart failure (DDEV v1.24.7+) and keeps working after upgrades. The entrypoint patches now match on patterns instead of line numbers. The rest updates the article for current DDEV: `x-ddev` keys label the services in `ddev describe`, `ddev ssh -s php -u devilbox` replaces a raw `docker exec`, and Step 2 is marked ARM64-only since AMD64 supports MySQL 5.5 directly. ## Manual Testing Instructions https://pr-738.ddev-com-fork-previews.pages.dev/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev/ Build the project from Steps 1 to 4 on Apple Silicon. `ddev mysql` should report `5.5.62-log`, `ddev restart` should not complain about a version mismatch, and `ddev ssh -s php -u devilbox` should land in a PHP 5.3 shell. ## Automated Testing Overview No tests needed, documentation-only change. ## Release/Deployment Notes No deployment changes are required. 🤖 Developed with assistance from [Claude Code](https://claude.ai/code) Co-authored-by: Claude Opus 5 --- ...th-unsupported-php-and-mysql-using-ddev.md | 74 ++++++++++++++----- 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md b/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md index 283bc9c74..41b339ad3 100644 --- a/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md +++ b/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md @@ -1,7 +1,8 @@ --- title: "Using DDEV to spin up a legacy PHP application" pubDate: 2025-05-30 -#modifiedDate: 2025-05-30 +modifiedDate: 2026-09-24 +modifiedComment: "Updated for current DDEV: the old `db` image now follows the installed DDEV version instead of a fixed one, the entrypoint patch matches the current `docker-entrypoint.sh`, and `x-ddev` keys label the services in `ddev describe`." summary: How to use DDEV with outdated PHP and MySQL versions author: Garvin Hicking featureImage: @@ -30,7 +31,7 @@ I quickly faced three major issues: Thanks to the outstanding DDEV support on Discord, I was quickly able to find a way with minimal effort, just by creating very small custom, additional docker-compose YAML files. -One advantage (of many) of using DDEV instead the underlying Docker Compose is that so many things are pre-configured and "just work". So I really did not want to migrate everything to Docker Compose on my own, do my custom routing, PHP-FPM integration and whatnot. +One advantage (of many) of using DDEV instead of the underlying Docker Compose is that so many things are pre-configured and "just work". So I really did not want to migrate everything to Docker Compose on my own, do my custom routing, PHP-FPM integration and whatnot. Just being able to "bait and switch" the PHP and DB container with a different base Docker image was all that was needed for me: @@ -38,47 +39,67 @@ Just being able to "bait and switch" the PHP and DB container with a different b I created the base `~/legacyphp/.ddev/config.yaml` file manually inside my `~/legacyphp` project directory, setting `legacyphp` as the project name. -Note that I configured PHP and MySQL versions that are supported by DDEV for this first: +MySQL 8.0 here is only a placeholder that DDEV accepts. Step 2 replaces it with MySQL 5.5. There is no `php_version`, because the PHP version in the `web` container does not matter: Step 3 adds a separate container for PHP 5.3. ```yaml +# .ddev/config.yaml name: legacyphp type: php docroot: htdocs -php_version: "8.3" webserver_type: apache-fpm database: type: mysql version: "8.0" +hooks: + pre-start: + - exec-host: | + DDEV_DB_LEGACY_IMAGE=$(echo "$DDEV_DBIMAGE" | sed 's/mysql-8.0/mysql-5.5/') + ddev dotenv set .ddev/.env.db.local --ddev-db-legacy-image="$DDEV_DB_LEGACY_IMAGE" >/dev/null ``` +The [`pre-start` hook](https://docs.ddev.com/en/stable/users/configuration/hooks/) keeps this setup up to date. `$DDEV_DBIMAGE` is the `db` image DDEV is about to use, for example `ddev/ddev-dbserver-mysql-8.0:v1.25.4`. Changing `mysql-8.0` to `mysql-5.5` gives the old image from the same DDEV release, so it stays in sync when you upgrade DDEV. + +[`ddev dotenv set`](https://docs.ddev.com/en/stable/users/usage/commands/#dotenv-set) saves that value in `.ddev/.env.db.local`, so the compose file in Step 2 can use it as `${DDEV_DB_LEGACY_IMAGE}`. + ## Step 2: Rewire DB +:::note +Only needed on ARM64. +::: + Next I created the very small file `~/legacyphp/.ddev/docker-compose.db.yaml` in the same directory next to `config.yaml`: ```yaml +# .ddev/docker-compose.db.yaml services: db: platform: linux/amd64 build: args: - BASE_IMAGE: ddev/ddev-dbserver-mysql-5.5:v1.24.6 + BASE_IMAGE: ${DDEV_DB_LEGACY_IMAGE} + image: ${DDEV_DB_LEGACY_IMAGE}-${DDEV_SITENAME}-built entrypoint: - sh - -c - | cp /docker-entrypoint.sh ~/docker-entrypoint.sh - sed -i '157s|.*|if false; then|' ~/docker-entrypoint.sh - sed -i '175s|.*|echo mysql_8.0 >/var/lib/mysql/db_mariadb_version.txt|' ~/docker-entrypoint.sh - sed -i '81i\ chmod -f -R u+w /etc/mysql/conf.d/* 2>/dev/null || true' ~/docker-entrypoint.sh + # report MySQL 8.0 so the version check passes + sed -i 's|server_db_version=.*|server_db_version=mysql_8.0|g' ~/docker-entrypoint.sh + sed -i 's|database_db_version=.*|database_db_version=mysql_8.0|g' ~/docker-entrypoint.sh + # but keep the real MySQL 5.5 configuration + sed -i 's|BEST_MATCH=.*|BEST_MATCH=/etc/mysql/version-conf.d/mysql_5.5.cnf.txt|g' ~/docker-entrypoint.sh exec ~/docker-entrypoint.sh + x-ddev: + describe-info: "Using mysql:5.5" ``` -Three things are noteworthy: +A few things are noteworthy: -- Setting `linux/amd64` as the platform will require Rosetta to be available on the macOS ARM64 platform -- The `BASE_IMAGE` is set to a DDEV `db` container of legacy Docker images that are still provided. -- Changing the `entrypoint` is a workaround to prevent DDEV complaining about a mismatching MySQL version after restarting the project. The small script "tricks" the DDEV inspection into believing, the version matches the one configured in `.ddev/config.yaml`. -- The `mysql-5.5:v1.24.6` image removes write permission from copied custom `.cnf` files, which can cause `Permission denied` when restarting an existing DB container; the added `chmod` makes this example restart-safe. This underlying issue was fixed in DDEV v1.24.7. +- Setting `linux/amd64` as the platform will require Rosetta to be available on the macOS ARM64 platform. +- `BASE_IMAGE` uses the `${DDEV_DB_LEGACY_IMAGE}` value from the hook, so the old image always matches your DDEV version. +- The `image` name stops the build from overwriting DDEV's own `ddev-dbserver-mysql-8.0` image on your machine. +- Changing the `entrypoint` stops DDEV from complaining about a version mismatch when you restart the project. The container runs MySQL 5.5, but has to look like the MySQL 8.0 set in `.ddev/config.yaml`. +- [`x-ddev`](https://docs.ddev.com/en/stable/users/extend/custom-docker-services/#customizing-ddev-describe-output) adds a note to `ddev describe`, so you can see the real version even though `.ddev/config.yaml` says 8.0. ## Step 3: Rewire PHP @@ -87,6 +108,7 @@ Using a different PHP version is just a few lines more work, because we are not This is done via the file `~/legacyphp/.ddev/docker-compose.php.yaml`: ```yaml +# .ddev/docker-compose.php.yaml services: php: container_name: ddev-${DDEV_SITENAME}-php @@ -108,19 +130,32 @@ services: - NEW_GID=${DDEV_GID} - DDEV_PHP_VERSION - IS_DDEV_PROJECT=true + x-ddev: + describe-url-port: | + PHP 5.3 shell: + ddev ssh -s php -u devilbox + describe-info: "Using PHP 5.3" + ssh-shell: bash web: + x-ddev: + describe-url-port: | + PHP 5.3 shell: + ddev ssh -s php -u devilbox + describe-info: "Using PHP 5.3" depends_on: - php ``` Note here that we use `devilbox/php-fpm` with our needed version, and a bind-mount takes care the PHP container can access our main project root directory. -A special mount of `~/legacyphp/.ddev/php/` is included so that we can control the `php.ini` configuration, if needed. For example you could disable the OPCache+APC in case you're doing some legacy benchmarking that should not be falsified via caching, I created a very small file `~/legacyphp/.ddev/php/php.ini` file with the contents: +The `x-ddev` blocks only change what `ddev describe` shows, but they save confusion later, because otherwise nothing hints that PHP runs in another container. The longer note goes in `describe-url-port` because that column is wider than `describe-info`. `ssh-shell: bash` is useful too, because custom services use `sh` by default. + +A special mount of `~/legacyphp/.ddev/php/` is included so that we can control the `php.ini` configuration, if needed. For example, you could disable OPcache and APC when doing legacy benchmarking that caching would distort. I created a very small `~/legacyphp/.ddev/php/php.ini` file with the contents: ```ini -# This is an example. -# apc.enabled=Off -# opcache.enable=Off +; .ddev/php/php.ini +apc.enabled=Off +opcache.enable=Off ``` ## Step 4: Utilize the PHP container with an Apache proxy @@ -128,6 +163,7 @@ A special mount of `~/legacyphp/.ddev/php/` is included so that we can control t To execute PHP with our external PHP Docker image, I created the following file in `~/legacyphp/.ddev/apache/apache-site.conf`: ```apache +# .ddev/apache/apache-site.conf RewriteEngine On RewriteCond %{HTTP:X-Forwarded-Proto} =https @@ -166,9 +202,11 @@ Your MySQL connection id is 5 Server version: 5.5.62-log MySQL Community Server (GPL) ``` +`ddev describe` shows the `x-ddev` notes from Steps 2 and 3, so you can see the real PHP and MySQL versions next to the services that run them. + ## Caveats -You can enter the PHP Docker container with a command like `docker exec -it -u devilbox ddev-legacyphp-php bash` if you need/want to execute PHP commands on shell-level, because the regular `web` container will run with the more recent PHP 8.3 version. +You can enter the PHP Docker container with `ddev ssh -s php -u devilbox` if you need to execute PHP commands at the shell level, because the regular `web` container runs a much more recent PHP version. So if you need to perform composer CLI calls, be sure to do this within the matching PHP container. Another thing to pay attention to is that if you for example want to utilize Mailpit with TYPO3's mail configuration, you can not use `localhost:1025` as an SMTP server. `localhost` in PHP's case will be that devilbox PHP container, and not the DDEV web container. Instead you need to setup `web:1025` as the hostname. From 46d5d00481ea1de5b5b44195c4e31ba37f4f7678 Mon Sep 17 00:00:00 2001 From: Stanislav Zhuk Date: Thu, 24 Sep 2026 17:37:20 +0300 Subject: [PATCH 3/4] fix apache config --- ...egacy-projects-with-unsupported-php-and-mysql-using-ddev.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md b/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md index 41b339ad3..3c2f1c2e7 100644 --- a/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md +++ b/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md @@ -172,6 +172,9 @@ To execute PHP with our external PHP Docker image, I created the following file SetEnvIf X-Forwarded-Proto "https" HTTPS=on Alias "/phpstatus" "/var/www/phpstatus.php" + + SetHandler "proxy:unix:/var/run/php/php-fpm.sock|fcgi://localhost" + DocumentRoot /var/www/html/htdocs AllowOverride All From 2600e33d1b377ba417e8a5fab6f0053920d792fe Mon Sep 17 00:00:00 2001 From: Stanislav Zhuk Date: Thu, 24 Sep 2026 17:37:48 +0300 Subject: [PATCH 4/4] comment out the image, it's not working yet --- ...legacy-projects-with-unsupported-php-and-mysql-using-ddev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md b/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md index 3c2f1c2e7..a8445813e 100644 --- a/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md +++ b/src/content/blog/legacy-projects-with-unsupported-php-and-mysql-using-ddev.md @@ -77,7 +77,7 @@ services: build: args: BASE_IMAGE: ${DDEV_DB_LEGACY_IMAGE} - image: ${DDEV_DB_LEGACY_IMAGE}-${DDEV_SITENAME}-built + #image: ${DDEV_DB_LEGACY_IMAGE}-${DDEV_SITENAME}-built entrypoint: - sh - -c