Converting wordpress/mpm_prefork/php/apache2 to wordpress/mpm_event/php-fpm/apache2

I am trying to convert an Wordpress-PHP-Apache2 (mpm_prefork) server to Wordpress-PHPFPM-Apache2 (mpm_event) server. The current docker environment has become much slower with increased concurrent access to Wordpress.

Current environment: Ubuntu 24.04.4, docker 28.1.1, WP 6.9.4, php 8.4, apache2 2.4, mariadb 10.11

docker-compose.yml:

volumes:
  wordpress:
    external: true
  wplogs:
    external: true
  mariadb_wp2:
    external: true
  mysql_tmp:
    external: true
  mysql_run:
    external: true

services:
  db:
    image: mariadb-chmod
    restart: always
    command: [
        '--lower_case_table_names=1',
        '--transaction-isolation=READ-COMMITTED',
        '--autocommit=1'
    ]
    environment:
      MYSQL_DATABASE:             bitnami_wordpress
      MYSQL_USER:                 bn_wordpress
      MYSQL_PASSWORD:             *********
      MYSQL_ROOT_USER:            root
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-u", "bn_wordpress", "--password=*********"]
      interval: 10s
      timeout: 5s
      retries: 5
    volumes:
      - mariadb_wp2:/var/lib/mysql
      - mysql_tmp:/tmp
      - mysql_run:/run/mysqld

  wordpress:
    image: wp-php-apache
    restart: always
    ports:
      - 443:443
    dns:
      - 81.130.111.248
      - 81.130.111.249
    depends_on: [ db ]
    environment:
      WORDPRESS_DB_HOST:          db
      WORDPRESS_DB_USER:          bn_wordpress
      WORDPRESS_DB_PASSWORD:      *********
      WORDPRESS_DB_NAME:          bitnami_wordpress
      WORDPRESS_AUTH_KEY:         ***************
      WORDPRESS_SECURE_AUTH_KEY:  ***************
      WORDPRESS_LOGGED_IN_KEY:    ***************
      WORDPRESS_NONCE_KEY:        ***************
      WORDPRESS_AUTH_SALT:        ***************
      WORDPRESS_SECURE_AUTH_SALT: ***************
      WORDPRESS_LOGGED_IN_SALT:   ***************
      WORDPRESS_NONCE_SALT:       ***************
      WORDPRESS_CONFIG_EXTRA: |
        define( 'FORCE_SSL_ADMIN', true );
        define( 'WP_TEMP_DIR', dirname(__FILE__) . '/wp-content/temp/' );
    healthcheck:
      test: ["CMD", "/usr/bin/healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 5
    volumes:
      - wordpress:/var/www/html
      - wplogs:/var/log/apache2
      - ./wp_000-default.conf:/etc/apache2/sites-available/000-default.conf:ro
      - ./default-ssl.conf:/etc/apache2/sites-available/default-ssl.conf:ro
      - ./mpm_prefork.conf:/etc/apache2/mpm_prefork.conf:ro
      - ./mpm_worker.conf:/etc/apache2/mpm_worker.conf:ro
      - ./mpm_event.conf:/etc/apache2/mpm_event.conf:ro
      - ./wp_ports.conf:/etc/apache2/ports.conf:ro
      - ./docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh:ro
      - /etc/lego/certificates/hollandnumerics.org.uk.crt:/etc/ssl/certs/hollandnumerics.org.uk.crt:ro
      - /etc/lego/certificates/hollandnumerics.org.uk.key:/etc/ssl/certs/hollandnumerics.org.uk.key:ro

mpm_prefork.conf:

StartServers              20
MaxRequestWorkers         40
MinSpareServers           1
MaxSpareServers           40
MaxConnectionsPerChild    0
ServerLimit               40
MaxClients                40

docker-entrypoint.sh:

#!/usr/bin/env bash
set -Eeuo pipefail

# Apache gets grumpy about PID files pre-existing
rm -f /var/log/apache2/httpd.pid

if [[ "$1" == apache2* ]] || [ "$1" = 'php-fpm' ]; then
	uid="$(id -u)"
	gid="$(id -g)"
	if [ "$uid" = '0' ]; then
		case "$1" in
			apache2*)
				user="${APACHE_RUN_USER:-www-data}"
				group="${APACHE_RUN_GROUP:-www-data}"

				# strip off any '#' symbol ('#1000' is valid syntax for Apache)
				pound='#'
				user="${user#$pound}"
				group="${group#$pound}"
				;;
			*) # php-fpm
				user='www-data'
				group='www-data'
				;;
		esac
	else
		user="$uid"
		group="$gid"
	fi

	if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then
		# if the directory exists and WordPress doesn't appear to be installed AND the permissions of it are root:root, let's chown it (likely a Docker-created directory)
		if [ "$uid" = '0' ] && [ "$(stat -c '%u:%g' .)" = '0:0' ]; then
			chown "$user:$group" .
		fi

		echo >&2 "WordPress not found in $PWD - copying now..."
		if [ -n "$(find -mindepth 1 -maxdepth 1 -not -name wp-content)" ]; then
			echo >&2 "WARNING: $PWD is not empty! (copying anyhow)"
		fi
		sourceTarArgs=(
			--create
			--file -
			--directory /usr/src/wordpress
			--owner "$user" --group "$group"
		)
		targetTarArgs=(
			--extract
			--file -
		)
		if [ "$uid" != '0' ]; then
			# avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted"
			targetTarArgs+=( --no-overwrite-dir )
		fi
		# loop over "pluggable" content in the source, and if it already exists in the destination, skip it
		# https://github.com/docker-library/wordpress/issues/506 ("wp-content" persisted, "akismet" updated, WordPress container restarted/recreated, "akismet" downgraded)
		for contentPath in \
			/usr/src/wordpress/.htaccess \
			/usr/src/wordpress/wp-content/*/*/ \
		; do
			contentPath="${contentPath%/}"
			[ -e "$contentPath" ] || continue
			contentPath="${contentPath#/usr/src/wordpress/}" # "wp-content/plugins/akismet", etc.
			if [ -e "$PWD/$contentPath" ]; then
				echo >&2 "WARNING: '$PWD/$contentPath' exists! (not copying the WordPress version)"
				sourceTarArgs+=( --exclude "./$contentPath" )
			fi
		done
		tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}"
		echo >&2 "Complete! WordPress has been successfully copied to $PWD"
	fi

	wpEnvs=( "${!WORDPRESS_@}" )
	if [ ! -s wp-config.php ] && [ "${#wpEnvs[@]}" -gt 0 ]; then
		for wpConfigDocker in \
			wp-config-docker.php \
			/usr/src/wordpress/wp-config-docker.php \
		; do
			if [ -s "$wpConfigDocker" ]; then
				echo >&2 "No 'wp-config.php' found in $PWD, but 'WORDPRESS_...' variables supplied; copying '$wpConfigDocker' (${wpEnvs[*]})"
				# using "awk" to replace all instances of "put your unique phrase here" with a properly unique string (for AUTH_KEY and friends to have safe defaults if they aren't specified with environment variables)
				awk '
					/put your unique phrase here/ {
						cmd = "head -c1m /dev/urandom | sha1sum | cut -d\\  -f1"
						cmd | getline str
						close(cmd)
						gsub("put your unique phrase here", str)
					}
					{ print }
				' "$wpConfigDocker" > wp-config.php
				if [ "$uid" = '0' ]; then
					# attempt to ensure that wp-config.php is owned by the run user
					# could be on a filesystem that doesn't allow chown (like some NFS setups)
					chown "$user:$group" wp-config.php || true
				fi
				break
			fi
		done
	fi
fi

a2enmod ssl
a2ensite default-ssl.conf
service apache2 restart
service apache2 stop

exec "$@"

wp-php-apache Dockerfile:

FROM wordpress:6.9.4-php8.4-apache

mariadb Dockerfile:

FROM mariadb:10.11
RUN chmod 1777 /tmp

What changes should be made to use php-fpm and mpm_event instead?

Thanks in advance…Phil

It took time to realize what your question was, but I think I get it now. So you should not use a container as a virtual machine. You either use the wordpress image that contains apache httpd and PHP as a module or use the PHP FPM variant of the wordpress image and use another HTTPD container container to connect to the PHP FPM container.

I have an old, currently not really maintained httpd image that you can check to get an idea. It was designed to be used with a PHP FPM container.

GitHub repo for the source code

I did not change the MPM settings, but that would be the same as without containers

Do you have a specific issue we can help you with and you could not solve?

Hi @rimelek,

I’ve tried to incorporate your httpd container into my server:

docker-compose.yml:

volumes:
  wordpress:
    external: true
  wplogs:
    external: true
  mariadb_wp2:
    external: true
  mysql_tmp:
    external: true
  mysql_run:
    external: true

services:
  db:
    image: mariadb-chmod
    restart: always
    command: [
        '--lower_case_table_names=1',
        '--transaction-isolation=READ-COMMITTED',
        '--autocommit=1'
    ]
    environment:
      MYSQL_DATABASE:             bitnami_wordpress
      MYSQL_USER:                 bn_wordpress
      MYSQL_PASSWORD:             *********
      MYSQL_ROOT_USER:            root
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-u", "bn_wordpress", "--password=*********"]
      interval: 10s
      timeout: 5s
      retries: 5
    volumes:
      - mariadb_wp2:/var/lib/mysql
      - mysql_tmp:/tmp
      - mysql_run:/run/mysqld

  wordpress:
    image: wp-php-fpm
    restart: always
    ports:
      - 9000:9000
    dns:
      - 81.130.111.248
      - 81.130.111.249
    depends_on: [ db ]
    environment:
      WORDPRESS_DB_HOST:          db
      WORDPRESS_DB_USER:          bn_wordpress
      WORDPRESS_DB_PASSWORD:      *********
      WORDPRESS_DB_NAME:          bitnami_wordpress
      WORDPRESS_AUTH_KEY:         ******************
      WORDPRESS_SECURE_AUTH_KEY:  ******************
      WORDPRESS_LOGGED_IN_KEY:    ******************
      WORDPRESS_NONCE_KEY:        ******************
      WORDPRESS_AUTH_SALT:        ******************
      WORDPRESS_SECURE_AUTH_SALT: ******************
      WORDPRESS_LOGGED_IN_SALT:   ******************
      WORDPRESS_NONCE_SALT:       ******************
      WORDPRESS_CONFIG_EXTRA: |
        define( 'FORCE_SSL_ADMIN', true );
        define( 'WP_TEMP_DIR', dirname(__FILE__) . '/var/www/html/wp-content/temp/' );
    healthcheck:
      test: ["CMD", "curl", "-k", "https://localhost/wp-login.php"]
      interval: 30s
      timeout: 10s
      retries: 5
    volumes:
      - wordpress:/var/www/html
      - ./www.conf:/usr/local/etc/php-fpm.d/www.conf:ro
      - ./docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh:ro

  web:
    image: webserver
    restart: always
    ports:
      - 443:443
    dns:
      - 81.130.111.248
      - 81.130.111.249
    depends_on: [ wordpress ]
    volumes:
      - wordpress:/var/www/html:ro
      - wplogs:/var/log/apache2
      - ./wp_000-default.conf:/usr/local/apache2/sites-available/000-default.conf:ro
      - ./default-ssl.conf:/usr/local/apache2/sites-available/default-ssl.conf:ro
      - ./mpm_prefork.conf:/usr/local/apache2/mpm_prefork.conf:ro
      - ./mpm_worker.conf:/usr/local/apache2/mpm_worker.conf:ro
      - ./mpm_event.conf:/usr/local/apache2/mpm_event.conf:ro
      - ./wp_ports.conf:/usr/local/apache2/ports.conf:ro
      - /etc/lego/certificates/hollandnumerics.org.uk.crt:/etc/ssl/certs/hollandnumerics.org.uk.crt:ro
      - /etc/lego/certificates/hollandnumerics.org.uk.key:/etc/ssl/certs/hollandnumerics.org.uk.key:ro
      - ./app-resources.sh:/usr/local/apache2/bin/app-resources.sh:ro
      - ./before-start.sh:/usr/local/apache2/bin/before-start.sh:ro
      - ./start.sh:/usr/local/apache2/bin/start.sh:ro

wp-php-fpm Dockerfile:

# switch to the FPM variant
FROM wordpress:6.9.4-php8.4-fpm

docker-entrypoint.sh:

#!/usr/bin/env bash
set -Eeuo pipefail

	if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then
		# if the directory exists and WordPress doesn't appear to be installed AND the permissions of it are root:root, let's chown it (likely a Docker-created directory)
		if [ "$uid" = '0' ] && [ "$(stat -c '%u:%g' .)" = '0:0' ]; then
			chown "$user:$group" .
		fi

		echo >&2 "WordPress not found in $PWD - copying now..."
		if [ -n "$(find -mindepth 1 -maxdepth 1 -not -name wp-content)" ]; then
			echo >&2 "WARNING: $PWD is not empty! (copying anyhow)"
		fi
		sourceTarArgs=(
			--create
			--file -
			--directory /usr/src/wordpress
			--owner "$user" --group "$group"
		)
		targetTarArgs=(
			--extract
			--file -
		)
		if [ "$uid" != '0' ]; then
			# avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted"
			targetTarArgs+=( --no-overwrite-dir )
		fi
		# loop over "pluggable" content in the source, and if it already exists in the destination, skip it
		# https://github.com/docker-library/wordpress/issues/506 ("wp-content" persisted, "akismet" updated, WordPress container restarted/recreated, "akismet" downgraded)
		for contentPath in \
			/usr/src/wordpress/.htaccess \
			/usr/src/wordpress/wp-content/*/*/ \
		; do
			contentPath="${contentPath%/}"
			[ -e "$contentPath" ] || continue
			contentPath="${contentPath#/usr/src/wordpress/}" # "wp-content/plugins/akismet", etc.
			if [ -e "$PWD/$contentPath" ]; then
				echo >&2 "WARNING: '$PWD/$contentPath' exists! (not copying the WordPress version)"
				sourceTarArgs+=( --exclude "./$contentPath" )
			fi
		done
		tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}"
		echo >&2 "Complete! WordPress has been successfully copied to $PWD"
	fi

	wpEnvs=( "${!WORDPRESS_@}" )
	if [ ! -s wp-config.php ] && [ "${#wpEnvs[@]}" -gt 0 ]; then
		for wpConfigDocker in \
			wp-config-docker.php \
			/usr/src/wordpress/wp-config-docker.php \
		; do
			if [ -s "$wpConfigDocker" ]; then
				echo >&2 "No 'wp-config.php' found in $PWD, but 'WORDPRESS_...' variables supplied; copying '$wpConfigDocker' (${wpEnvs[*]})"
				# using "awk" to replace all instances of "put your unique phrase here" with a properly unique string (for AUTH_KEY and friends to have safe defaults if they aren't specified with environment variables)
				awk '
					/put your unique phrase here/ {
						cmd = "head -c1m /dev/urandom | sha1sum | cut -d\\  -f1"
						cmd | getline str
						close(cmd)
						gsub("put your unique phrase here", str)
					}
					{ print }
				' "$wpConfigDocker" > wp-config.php
				if [ "$uid" = '0' ]; then
					# attempt to ensure that wp-config.php is owned by the run user
					# could be on a filesystem that doesn't allow chown (like some NFS setups)
					chown "$user:$group" wp-config.php || true
				fi
				break
			fi
		done
	fi
fi

# start PHP-FPM in the background
php-fpm -D

exec "$@"

All 3 containers have been created, but only the mariadb-chmod and webserver containers start successfully, and wp-php-fpm repeatedly restarts.

What have I missed?..Phil

PS. How do I look at a log from the wp-php-fpm container that keeps restarting?

I have now resolved the “restarting” of the wp-php-fpm container by copying the docker-entrypoint.sh file from the corresponding Docker Wordpress image.

However, now I am getting the following messages in the wp-php-fpm container log:

{"log":"[31-May-2026 10:26:22] NOTICE: fpm is running, pid 1\n","stream":"stderr","time":"2026-05-31T10:26:22.893922333Z"}
{"log":"[31-May-2026 10:26:22] NOTICE: ready to handle connections\n","stream":"stderr","time":"2026-05-31T10:26:23.167173779Z"}

But the browser connection shows the following 503 message:

Service Unavailable
The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.

The webserver container log included the following messages:

{"log":"[Sun May 31 10:30:37.575767 2026] [proxy:error] [pid 212:tid 224] (111)Connection refused: AH00957: FCGI: attempt to connect to 172.21.0.3:9000 (wordpress:9000) failed\n","stream":"stderr","time":"2026-05-31T10:30:37.575944233Z"}
{"log":"[Sun May 31 10:30:37.575801 2026] [proxy_fcgi:error] [pid 212:tid 224] [client 192.168.1.1:50652] AH01079: failed to make connection to backend: wordpress\n","stream":"stderr","time":"2026-05-31T10:30:37.576016127Z"}
{"log":"192.168.1.1 - - [31/May/2026:10:30:37 +0000] \"GET /wp-admin/ HTTP/1.1\" 503 339\n","stream":"stdout","time":"2026-05-31T10:30:37.576077838Z"}

Are you sure fpm still listens on port 9000?

I think port 9000 is still being used by fpm:

  • wordpress image is based on php:8.4-fpm
  • php:8.4-fpm image includes EXPOSE 9000

However, I had changed the wordpress image from wordpress:6.9.4-php8.4-fpm to wordpress:7.0-php8.4-fpm

Looking in the webserver Dockerfile, are these environment variables set appropriately?

    SRV_REVERSE_PROXY_DOMAIN="" \
    SRV_REVERSE_PROXY_CLIENT_IP_HEADER="X-Forwarded-For" \
    SRV_PHP="true" \
    SRV_PHP_DISABLE_REUSE="true" \
    SRV_PHP_HOST="wordpress" \
    SRV_PHP_PORT="9000" \
    SRV_PROXY_PROTOCOL="false" \
    SRV_PROXY_FORWARD_TO="" \
    SRV_PROXY_FORWARD_FROM="/"

Sorry for the slow responses.

The variables look right. Those are the default values in my image, but you don’t need most of those variables.

Try to get the generated httpd config to confirm if it is correct. If not, you can investigate why.

But the logs showed that http tries to use the correct host. If the port is correct too and PHP FPM is still running, that should work.

You could try running a container for debugging and connect it to the compose network used by the PHP service. Something like this:

network_name=projectname-default
docker run --rm -it --network "$network_name"  nicolaka/netshoot

Then try the following commands:

nslookup wordpress
telnet wordpress 9000
# type something and press enter

If that doesn’t work, try this too:

container_name=projectname-wordpress-1
docker run --rm -it --network "container:$container_name"  nicolaka/netshoot

and

telnet 127.0.0.1 9000
# type something and press enter

I didn’t try these commands so could be wrong, but I hope it still helps. I wonder if the issue is caused by dns resolution or fpm not listening on the right IP address. Maybe only on localhost. So while you are still in the container conected to the container network, not the bridge, you can also try

ss -tulpn | grep 9000

That shows if the wordpress container is not correctly listening on the IP address of the container.

NOTE: Don’t forget to change the variable values to your actual network name or container name.

UPDATE: THe port numnber in the last grep was wrong. IIt should have been 9000. fixed it.

Thank you.

I’m going to have to take a longer look into this…

Let me know if you can get some useful info from the outputs of the recommended commands and still need help. I also fixed the last code block where I used the wrong port tnumber.

Hi @rimelek,

I’ve been away from my office, so could not test your suggestions until now.

I haven’t changed any code, but running:

docker run --rm -it --network "wordpress-fpm-docker-server_default" nicolaka/netshoot

then

nslookup wordpress
telnet wordpress 9000

gives

 25031f5ecf7b  ~  nslookup wordpress
Server:         127.0.0.11
Address:        127.0.0.11#53

Non-authoritative answer:
Name:   wordpress
Address: 172.18.0.3

 25031f5ecf7b  ~  telnet wordpress 9000
telnet: can't connect to remote host (172.18.0.3): Connection refused

You stated that you had changed the last code block, but what did you actually change?

…Phil

Hi @rimelek ,

I also tried running:

docker run --rm -it --network "container:wordpress-fpm-docker-server-wordpress-1" nicolaka/netshoot
telnet 127.0.0.1 9000
pwd
ls -al

which gives:

 d0825bdfa9e8  ~  telnet 127.0.0.1 9000
Connected to 127.0.0.1
Connection closed by foreign host

 d0825bdfa9e8  ~  pwd
/root

 d0825bdfa9e8  ~  ls -al
total 160
drwxrwx---    1 root     root          4096 Jun 19 09:53 .
drwxr-xr-x    1 root     root          4096 Jun 19 09:52 ..
drwxr-xr-x    3 root     root          4096 Jun 19 09:52 .cache
drwxrwxr-x    1 root     root          4096 Jan  7 17:07 .oh-my-zsh
-rw-r--r--    1 root     root         36713 Jun 19 09:52 .zcompdump-d0825bdfa9e8-5.9
-r--r--r--    1 root     root         83928 Jun 19 09:52 .zcompdump-d0825bdfa9e8-5.9.zwc
-rw-------    1 root     root            96 Jun 19 09:53 .zsh_history
-rw-rw-r--    1 root     root          4626 Jan  7 17:07 .zshrc
-rw-rw-r--    1 root     root           592 Jan  7 17:07 motd

and ss -tulpn gives:

 d0825bdfa9e8  ~  ss -tulpn
Netid         State           Recv-Q          Send-Q                   Local Address:Port                    Peer Address:Port         Process         
udp           UNCONN          0               0                           127.0.0.11:35553                        0.0.0.0:*                            
tcp           LISTEN          0               4096                        127.0.0.11:46823                        0.0.0.0:*                            
tcp           LISTEN          0               4096                         127.0.0.1:9000                         0.0.0.0:*   

so it appears to be listening on the correct port.

Does this help?..Phil

Hi @rimelek ,

Does the fact that telnet can access the container directly, but not via the network mean anything?

Should I update the docker-compose.yml file to improve this?

…Phil

You can see it when you click on the pencil icon in the top right corner of the post. I described the change also in the end of the post in the last line started with “UPDATE:”. It was only a wrong port number instead of port 9000.

Correct port, yes, but not the correct IP. It looks like it listens only on the loopback IP, so it will not be available from anywhere but the same container.

It means exactly what I suspected here:

It would only work with 127.0.0.1 if the HTTPD used the same network namespace as PHP FPM. You can try this:

web:
  network_mode: service:wordpress

It will do the same as you did with the nicolaka/netshoot container to connect to the wordpress network, but this syntax in compose can refer to the network namespace of other services with the service: prefix. You would also need to change the Apache HTTPD config to use “localhost” or the 127.0.0.1 instead of wordpress in the PHP FPM connection parameter.

Why your PHP FPM doesn’t listen on the right IP, I am not sure.

Hi @rimelek ,

I can see the update to 9000, but I’d ignored it to show all ports! Sorry.

  1. Changed “wordpress:9000” to “172.18.0.3:9000” in Dockerfile and WordPress site opened, but none of the links worked!

  2. Checked SRV_DOCROOT=“/var/www/html” existed in webserver Dockerfile, but updated app-resources.sh to force /var/www/html, because this was not the default. Also all files have ownership www-data:www-data:

getDocRoot () {
    local DOC_ROOT="/var/www/html"; #PRH
   # local SAVED_DOC_ROOT="";
   # if \[ -f "${HOME}/httpd-docroot" \]; then
   #     SAVED_DOC_ROOT="$(cat "${HOME}/httpd-docroot")";
   #     if \[ -n "${SAVED_DOC_ROOT}" \]; then
   #         DOC_ROOT="${SAVED_DOC_ROOT}";
   #     fi;
   # fi;
    echo "${DOC_ROOT}"
};
  1. Now getting:
    Forbidden
    You don’t have permission to access this resource.

  2. In the Docker log I’m seeing:
    AH01630: client denied by server configuration: /var/www/html/wp-admin/admin-ajax.php

  3. What should I have done to direct DOC_ROOT to /var/www/html?

I feel I’m close, but have no idea what to do next…Phil

You probably meant compose file, not Dockerfile. And I actualy recommended 127.0.0.1 not 172.18.0.3. That can change, but you just reused the wordpress namespace exactly because PHP-FPM listened only on the loopback IP, so I am quite confused now.

When links don’t work that is a client side issue that you can debug by opening the developer console n your browser. You can chck the network tab and see what files cannot load. Or just check the URLs of the links. Wordpress has to be configured to use the right domain or base url if it cannot be detected automatically. We had many discussions about that on the forum, but I always forget the exact Wordpress configuration as I haven’t used wordpress for many years. I remember there must be a variable, but sometimes you may need to delete some cache in the database to use the right URL.

Sorry, I’m confused again. The variable was meant to be set in a compose file or any way depending on how you create the container. The default can be anything. You just need to set it for the container. If you used the same image or same scripts I used in my image, these examples in the documentation should help

https://httpd24-docker-image.readthedocs.io/en/latest/examples.html#simplest-way-to-use-php-fpm

SRV_DOCROOT is not in the examples, but the way to set it is the same. It is just not common to change it, because you can mount the volume to wahever path you prefer.

I can recommend the PHP-FPM examples in the docs:

https://httpd24-docker-image.readthedocs.io/en/latest/examples.html#simplest-way-to-use-php-fpm

You can try a simple PHP image without wordpress. When that works and you understand how it works, you can try with a wordpress image again. That way you know if the problem is FPM setting or something Wordpress -related

Not sure. But the webserver usually runs as “daemon” and the PHP as “www-data”. The name itself doesn’t matter but the ID behind them. But that is also different usually. So if that file or folder is accessible only by www-data that would block access to the file from the webserver if it wants to load it. Why it would do that I’m not sure. I didn’t need to operate HTTPD servers recently, but PHP files are not even required in httpd containers unless the server is configured to check the existence of the files before asking PHP-FPM to process them.

If you search for the error message, you can find this in the apache httpd wiki:

The scripts in my HTTPD image handle related configurations as much as it is possible, but if you just change the script, I don’t remember how good I made that image, but after checking the shell script, I see that just the function above the one that you edited uses the return value of getDocRoot to modify the Directory block to allow accessing files under the new docroot. You can check the generated httpd config files to see if it is correct and not referring to the wrong docroot.

Or you can also just mount the volume to the default docroot in the webserver container.