Deploying PHP with nginx + php-fpm in a single container

I recently had to do some maintenance work on an old application hosted on the CakePHP server. The CakePHP project has 20 or so sites and applications deployed in dokku. Dokku allows us to build a platform-as-a-service for our sites that operates on a single server. Dokku encourages using heroku-style ‘buildpacks’ to deploy applications. Buildpacks give you a very simple way to deploy applications as buildpacks require very little configuration or setup. Buildpacks are great for applications that run in supported PHP versions. However, the application I wanted to change only supports PHP 7, which is end-of-life. This left me in a tough spot as I couldn’t build new images for this application with buildpacks.

I also didn’t want to invest the time to upgrade this application across major framework and PHP versions. Instead, I wanted to leave the application code alone and instead leverage the benefits of a containerized deployment by being to continue deploying this application with PHP7. Thankfully Dokku supports docker based applications, so all I needed to do was get a docker image created.

Apache or nginx + FPM?

Deploying a PHP application requires both a PHP runtime and an HTTP server. Using Apache enables both of these components to be bundled together as Apache can be compiled with mod_php support. Another common other option is to run nginx and php-fpm. I wanted to be consistent with our other applications and use nginx and php-fpm but was having a hard time finding a good example of building these into a single container.

My solution

My solution to this problem was to base my application container off of the php:7.4-fpm image, and layer nginx into it. The standard docker images provide good tools for installing additional extensions which I would need. When the container was started, I would run php-fpm in the background and nginx. My Dockerfile ended up like:

Show Plain Text
  1. FROM docker.io/library/php:7.4-fpm
  2.  
  3. # Install additional extensions and nginx
  4. RUN apt-get update \
  5.   && apt-get install -y libicu67 libicu-dev libzip4 libzip-dev nginx \
  6.   && docker-php-ext-install pdo pdo_mysql intl pcntl zip
  7.  
  8. # Setup nginx site
  9. COPY ./nginx.conf /etc/nginx/sites-available/default
  10.  
  11. # Install composer
  12. RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
  13.   && chmod +x /usr/local/bin/composer
  14.  
  15. # Copy php.ini in
  16. RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
  17.  
  18. # Copy application code into the container
  19. COPY . /opt/app
  20.  
  21. # Install dependencies and symlink webroot.
  22. RUN cd /opt/app \
  23.   && /usr/local/bin/composer install --no-dev --no-interaction \
  24.   && rm -r /var/www/html \
  25.   && ln -s /opt/app/webroot/ /var/www/html
  26.  
  27. STOPSIGNAL SIGTERM
  28.  
  29. # Dokku wanted port 5000 for compatibility with buildpacks
  30. EXPOSE 5000
  31.  
  32. # Start both php-fpm and nginx
  33. CMD "/opt/app/run.sh"

Because I needed to use nginx to handle the HTTP traffic and forward requests to php-fpm over FastCGI, I used the following nginx site configuration:

Show Plain Text
  1. server {
  2.   listen 5000;
  3.   server_name localhost;
  4.   root /var/www/html;
  5.  
  6.   index index.php;
  7.  
  8.   location / {
  9.     try_files $uri $uri/ /index.php?$args;
  10.   }
  11.  
  12.   location ~ [^/]\.php(/|$) {
  13.     fastcgi_split_path_info ^(.+?\.php)(/.*)$;
  14.     if (!-f $document_root$fastcgi_script_name) {
  15.       return 404;
  16.     }
  17.  
  18.     include fastcgi_params;
  19.     fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  20.     fastcgi_param PATH_INFO       $fastcgi_path_info;
  21.     fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
  22.  
  23.     fastcgi_pass   localhost:9000;
  24.     fastcgi_index  index.php;
  25.   }
  26. }

Next, the /opt/app/run.sh script starts up php-fpm and nginx and looks like:

Show Plain Text
  1. #!/bin/bash
  2. echo 'starting php-fpm in background'
  3. php-fpm &
  4.  
  5. echo 'starting nginx'
  6. nginx -g 'daemon off;'

With all of this taken care of, I was able to push the application to Dokku via git push dokku and have nginx + php-fpm running in a single container.

Comments

There are no comments, be the first!

Have your say: