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:
- FROM docker.io/library/php:7.4-fpm
- # Install additional extensions and nginx
- RUN apt-get update \
- && apt-get install -y libicu67 libicu-dev libzip4 libzip-dev nginx \
- && docker-php-ext-install pdo pdo_mysql intl pcntl zip
- # Setup nginx site
- COPY ./nginx.conf /etc/nginx/sites-available/default
- # Install composer
- RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
- && chmod +x /usr/local/bin/composer
- # Copy php.ini in
- RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
- # Copy application code into the container
- COPY . /opt/app
- # Install dependencies and symlink webroot.
- RUN cd /opt/app \
- && /usr/local/bin/composer install --no-dev --no-interaction \
- && rm -r /var/www/html \
- && ln -s /opt/app/webroot/ /var/www/html
- STOPSIGNAL SIGTERM
- # Dokku wanted port 5000 for compatibility with buildpacks
- EXPOSE 5000
- # Start both php-fpm and nginx
- 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:
- server {
- listen 5000;
- server_name localhost;
- root /var/www/html;
- index index.php;
- location / {
- try_files $uri $uri/ /index.php?$args;
- }
- location ~ [^/]\.php(/|$) {
- fastcgi_split_path_info ^(.+?\.php)(/.*)$;
- if (!-f $document_root$fastcgi_script_name) {
- return 404;
- }
- include fastcgi_params;
- fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
- fastcgi_param PATH_INFO $fastcgi_path_info;
- fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
- fastcgi_pass localhost:9000;
- fastcgi_index index.php;
- }
- }
Next, the /opt/app/run.sh
script starts up php-fpm
and nginx
and looks like:
- #!/bin/bash
- echo 'starting php-fpm in background'
- php-fpm &
- echo 'starting nginx'
- 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.
There are no comments, be the first!