Dockerize any PHP project. A pure bash script.

Max Kotliar
formapro
Published in
3 min readMar 31, 2017

--

In the previous blog posts we discussed several options on how to run a PHP project in a docker container(s). The first one is about docker compose file, the second is about about Supervisord which runs Nginx and PHP-FPM. We tried to highlight their pros and cons.

In this blog post we show one more solution on how to dockerize a PHP application. The goal is still the same to build a drop in container, you copy our app into it and it just works.

If you prefer to see the code than read, here it is formapro/nginx-php-fpm.

Docker’s best practise is to run a single process inside a container. That’s indeed pretty logical advice but does not work for PHP apps (You can find out why in the previous blog post).

The solution is based on a shell script does all the magic. We tried to pack Nginx, PHP-FPM into a single container (like in Supervisord approach, but without it (: ) with some default configurations.

Here’s how you can run a PHP file behind Nginx and PHP-FPM:

echo "<?php phpinfo();" > app.phpdocker run -d -p 8080:80 -v `pwd`:/var/www/html formapro/nginx-php-fpm curl -X GET localhost:8080 # runs app.php and outputs phpinfo

Now let’s see what’s inside. When the container starts it has to run two processes:

# /entrypoint.shnginx -c /etc/nginx/nginx.conf -g ‘daemon off;’ 2>&1 &
NGINX_PID=$!
php-fpm7.0 -R -F -c /etc/php/7.0/fpm/php-fpm.conf 2>&1 &
PHP_FPM_PID=$!
wait $NGINX_PID $PHP_FPM_PID

That works but it does not cover some edge cases.

The script does not handle signals correctly. For example if SIGTERM is sent to a container it has to gracefully stop a running process and exit but in our case bash immediately exits. PHP-FPM and Nginx do not have a chance to stop correctly. That could be solved by using trap command which allows to define a script to execute on a signal.

# /entrypoint.shtrap "kill -15 $NGINX_PID; kill -15 $PHP_FPM_PID;" SIGTERM  SIGINT

What if one of those processes exits, the container must exit but instead wait command still waits for another process and we end up having half working container. We were able to found the following solution to the problem. We had to replace wait with while loop and some ifs.

# /entrypoint.shwhile :
do
kill -0 $NGINX_PID 2> /dev/null
NGINX_STATUS=$?
kill -0 $PHP_FPM_PID 2> /dev/null
PHP_FPM_STATUS=$?
if [ $NGINX_STATUS -ne 0 ] || [ $PHP_FPM_STATUS -ne 0 ]; then
if [ $NGINX_STATUS -eq 0 ]; then
kill -15 $NGINX_PID;
wait $NGINX_PID;
fi
if [ $PHP_FPM_STATUS -eq 0 ]; then
kill -15 $PHP_FPM_PID;
wait $PHP_FPM_PID;
fi
exit 1;
fi
sleep 1
done

It checks whether processes are still alive and if one of them has crashed or exited it stops another process.

As we are Symfony developers we’d like to adjust the container to serve Symfony application easily. Here’s example that shows how to change the web root directory:

# /entrypoint.shexport NGINX_WEB_ROOT=${NGINX_WEB_ROOT:-'/var/www/html'}
export NGINX_CONF=${NGINX_CONF:-'/etc/nginx/nginx.conf'}
envsubst '${NGINX_WEB_ROOT}' < /tmp/nginx.conf.tpl > $NGINX_CONFnginx -c $NGINX_CONF -g ‘daemon off;’ 2>&1 &

It makes use of handy util called envsubst. It takes a template files with env vars as placeholders and replaces them with their real values. The file stored in a new location. The directory could be customized by adding an env docker option:

docker run -p 80:80 -v `pwd`:/app --env NGINX_WEB_ROOT=/app/web formapro/nginx-php-fpm

The container is present on docker hub. The source code is on github.

--

--