Kubernetes: Nginx and PHP-FPM

When we want to use PHP-FPM, in most cases, we also need to use some of the web servers. Typical choice is Nginx because it is a very light, reliable, and configurable solution which can handle even very big traffic without any issues. There are two methods of linking Nginx and PHP-FPM on the backend: TCP connections and UNIX sockets. In this post, I would like to share them in term of Kubernetes configuration. It can be applied also on standard Docker solutions without issues.

Why do they need to be linked?

First, i should explain why both services must be linked. Nginx cannot handle PHP requests automatically; it does not have built-in PHP interpreter and needs something to handle them using FCGI. On the other side, PHP does not have proper built-in web server. The only one available in default is very simple and should be use only locally for debugging, nothing more. Some scenarios allow us to use PHP without Nginx, for example FrankenPHP, but it means we need to also move all requests logic and rules to PHP level, and it may be not a good solution. Instead, we link both services and pass requests related to PHP files from Nginx to PHP-FPM and then sent them back to the client.

TCP/IP Connection

The simplest and also default option is to use standard TCP/IP connections, it is even a default option. In this scenario PHP-FPM uses one specific port per each daemon (we can use multiple with different settings let’s say per website) to expose its functionality. It is listening on this port. At the same time, we can configure Nginx to push PHP-related requests to this port to handle them properly.

First, let’s ensure we have proper port configuration on PHP-FPM. As I mentioned, it is default configuration so probably we do not need to change anything on this side:

listen = 127.0.0.1:9000

After, we can add some changes into our Nginx configuration. It is a very simple example, and it does not include all params – please keep this in mind:

location ~ \.php$ {
    include fastcgi-params.conf;
    fastcgi_pass 127.0.0.1:9000;
}

This solution will work without any additional changes, just simple and fine. The problem is with the bigger scale. TCP connections are not bad, but it is a lot of additional network traffic inside our system and of course additional work to “pack” and “unpack” all data. If we need to handle a lot of PHP requests, it will not be the best solution and we should jump into UNIX sockets solution.

UNIX Sockets

With UNIX sockets, everything is a bit more complex. It is because they are not a part of the networking system and handle things differently. As I mentioned before, TCP connections are available for other containers without issues, sockets are not. They are visible as files, so to expose them, we need to store them in some place available for both containers. Shared volume will be the best place, so we will use this option.

First, create a new shared volume and configure both containers to use it. We can use any directories we want to map them into containers. Please remember it is very short example without all other settings:

volumes:
- name: shared-data
  emptyDir: {}
  
containers:
- name: nginx-container
  volumeMounts:
  - name: shared-data
    mountPath: /var/run/php

- name: php-fpm-container
  volumeMounts:
  - name: shared-data
    mountPath: /var/run

Then, change the PHP-FPM configuration to use UNIX socket and save it on the shared volume:

listen = /var/run/php-fpm.sock

Finally, modify nginx to also use shared volume to put proper requests to our new UNIX socket:

location ~ \.php$ {
    include fastcgi-params.conf;
    fastcgi_pass /var/run/php/php-fpm.sock;
}

And it is all. After deploying changes, both services will be linked and work correctly. The same approach may be used also in standard Docker