Install and Configure Apache HTTP Server with PHP-FPM on CentOS Stream 9: Practical Performance Optimization

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

Context and Why Apache + PHP-FPM is Needed on CentOS Stream 9

When building a web server, we often face a difficult problem: how to balance high performance and stability? Apache HTTP Server, with its long development history and proven reliability, remains a top choice for many. However, for modern PHP applications, directly using Apache’s mod_php module is sometimes not the most optimal approach in terms of both performance and security.

This is where PHP-FPM (FastCGI Process Manager) comes into play. PHP-FPM operates as an independent process, managing separate PHP “workers”.

When Apache receives a request to process a PHP file, it forwards that request to PHP-FPM. This mechanism not only helps completely separate the web server and PHP processes, enhancing security (each website can run under its own PHP-FPM user), but also significantly improves performance, especially with “heavy” PHP applications or when traffic is high, for example, an e-commerce site with thousands of requests per minute.

Regarding CentOS Stream 9, I personally consider this an interesting move from Red Hat. I once “struggled” when migrating several CentOS 7 servers to AlmaLinux 9, so I understand the differences between versions very well.

Choosing CentOS Stream 9 for new systems, in my opinion, helps you access new features earlier while maintaining stability and compatibility with the RHEL ecosystem. This is an ideal choice for development and testing environments before official deployment. It helps us proactively familiarize ourselves with upcoming changes, avoiding a passive situation like when CentOS 8 suddenly reached its end of life.

Install Necessary Components

Before starting, update the system to ensure all software packages are up-to-date, avoiding conflict errors:


sudo dnf update -y

Install Apache HTTP Server

On CentOS Stream 9, Apache HTTP Server is called httpd. Installation is very simple:


sudo dnf install httpd -y
sudo systemctl enable --now httpd
sudo systemctl status httpd

After installation, check the service status. If you see active (running), it means everything is ready. Don’t forget to open ports 80 (HTTP) and 443 (HTTPS) on the firewall so the web server can operate:


sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Install PHP-FPM

CentOS Stream 9 provides PHP in its default package repositories, but it’s often not the latest version or lacks necessary extensions. To get the most up-to-date PHP-FPM and extensions, I usually use the EPEL and Remi repositories.


sudo dnf install epel-release -y
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm -y

After adding the Remi repository, you can view the list of available PHP versions. Currently, PHP 8.2 and 8.3 are popular versions:


sudo dnf module list php

To install PHP 8.2 (for example), you need to enable the corresponding module and install PHP-FPM along with important extensions. I usually install the following modules: php-cli, php-mysqlnd, php-gd, php-xml, php-mbstring, php-fpm, php-opcache, php-json.


sudo dnf module enable php:remi-8.2 -y
sudo dnf install php-fpm php-cli php-mysqlnd php-gd php-xml php-mbstring php-opcache php-json -y

Finally, enable and check the status of PHP-FPM:


sudo systemctl enable --now php-fpm
sudo systemctl status php-fpm

Detailed Apache Configuration for Working with PHP-FPM

This is a crucial step where we will “connect” Apache and PHP-FPM.

Configure Apache Virtual Host

We need to create a Virtual Host to serve your website. I recommend creating a separate configuration file for each Virtual Host in the /etc/httpd/conf.d/ directory. For example, for a website with the domain example.com:


sudo vi /etc/httpd/conf.d/example.com.conf

The content of the example.com.conf file will be similar to this:


<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/public_html

    <Directory /var/www/example.com/public_html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # Enable ProxyPassMatch to forward PHP requests to PHP-FPM
    # PHP-FPM usually listens on port 9000 or via Unix socket.
    # CentOS Stream 9 defaults to using Unix socket: /run/php-fpm/www.sock
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost/"
    </FilesMatch>

    ErrorLog /var/log/httpd/example.com_error.log
    CustomLog /var/log/httpd/example.com_access.log combined
</VirtualHost>

To help you visualize, let’s explain the line SetHandler "proxy:unix:/run/php-fpm/www.sock|fcgi://localhost/":

  • proxy:unix:/run/php-fpm/www.sock: Apache will use the mod_proxy_fcgi module to connect to PHP-FPM via a Unix socket at the path /run/php-fpm/www.sock. This method is both more efficient and more secure than using traditional TCP ports.
  • fcgi://localhost/: This is a crucial component that helps PHP-FPM correctly identify the document root of the request. When PHP-FPM receives a request, it will use this part to accurately set the SCRIPT_FILENAME environment variable.

Next, create the DocumentRoot directory and set correct permissions:


sudo mkdir -p /var/www/example.com/public_html
sudo chown -R apache:apache /var/www/example.com
sudo chmod -R 755 /var/www/example.com

Check Apache configuration syntax and restart the service to apply changes:


sudo apachectl configtest
sudo systemctl restart httpd

Configure PHP-FPM Pool

PHP-FPM has a default pool named www. For simple setups, you can certainly use this pool. Its configuration file is located at /etc/php-fpm.d/www.conf.

Check the value of the listen line in the www.conf file:


grep "listen =" /etc/php-fpm.d/www.conf

If the result is listen = /run/php-fpm/www.sock, then our Apache configuration above is correct. If you see a TCP port (e.g., listen = 127.0.0.1:9000), you will need to adjust the SetHandler line in the Apache configuration to "proxy:fcgi://127.0.0.1:9000" accordingly.

Additionally, you should fine-tune the following parameters to optimize performance and prevent errors during operation:

  • user and group: Set both to apache. This helps PHP-FPM run under the same user as Apache, thoroughly resolving file access permission issues, especially when interacting with files created by Apache.
  • Process management parameters (pm.*):
    • pm = dynamic: This is the default mode and is suitable for most use cases.
    • pm.max_children: This is the maximum number of PHP-FPM child processes allowed to run. You need to consider this carefully based on the server’s RAM capacity. A small rule of thumb is: divide the total RAM dedicated to PHP by the average RAM consumed by one PHP process (for example, if you dedicate 2GB RAM to PHP and each PHP process uses about 40MB, you can set pm.max_children to around 50).
    • pm.start_servers, pm.min_spare_servers, pm.max_spare_servers: These parameters help FPM automatically adjust the number of processes to “scale” according to the load, ensuring the server is not overloaded but still responds quickly.

Example adjustments in the /etc/php-fpm.d/www.conf file (only modify necessary lines):


listen = /run/php-fpm/www.sock
listen.owner = apache
listen.group = apache
listen.mode = 0660

user = apache
group = apache

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35

After completing the changes, restart PHP-FPM for the new configurations to take effect:


sudo systemctl restart php-fpm

SELinux Configuration

CentOS Stream 9 enables SELinux by default. This is a powerful security layer, but it can sometimes hinder newcomers. For Apache and PHP-FPM to communicate via a Unix socket, we must ensure SELinux allows this connection.

If you encounter a “Permission denied” error in Apache’s logs when trying to connect to PHP-FPM, check the SELinux logs. Typically, you will need to create a custom policy to allow this interaction:


sudo ausearch -c 'httpd' --raw | sudo audit2allow -M my-httpd
sudo semodule -i my-httpd.pp

In case you need a quick troubleshooting fix (for testing environments only!), you can temporarily switch SELinux to Permissive mode:


sudo setenforce 0

To return to Enforcing mode after troubleshooting: sudo setenforce 1. Note that disabling SELinux is not recommended for production environments.

Testing and Monitoring

After installation and configuration are complete, verifying that everything is working smoothly is extremely important.

Test PHP Operation

Create a simple PHP file in the DocumentRoot directory of your Virtual Host:


sudo vi /var/www/example.com/public_html/info.php

Content of the info.php file:


<?php
phpinfo();
?>

Open your browser and access http://example.com/info.php. If you see the PHP information page appear, especially with “Server API” showing “FPM/FastCGI”, congratulations! Apache and PHP-FPM are successfully working together.

Check Logs

When encountering issues, log files are your best friends. Don’t forget to check the following log files:

  • Apache Logs: /var/log/httpd/error_log and /var/log/httpd/example.com_error.log (for a specific Virtual Host).
  • PHP-FPM Logs: /var/log/php-fpm/www-error.log (or the log file corresponding to the pool you are using).

Use the tail -f command to view logs in real-time when you access the website; this helps you detect errors immediately:


sudo tail -f /var/log/httpd/example.com_error.log
sudo tail -f /var/log/php-fpm/www-error.log

Basic Monitoring

To maintain stable performance, you need to closely monitor system resources such as CPU, RAM, and I/O, especially when the website is under high load. Tools like htop, glances, or sar will provide useful overviews. One point to note is the number of running php-fpm processes, which helps you adjust the pm.* parameters to suit the server’s resources and actual traffic.


htop

If you see PHP-FPM processes being continuously killed and restarted, or the website shows 50x errors, this is a clear indication that you need to adjust the pm.max_children value or reconsider the memory usage of each PHP process.

Setting up Apache with PHP-FPM on CentOS Stream 9 might seem complex at first, but with meticulous attention to each configuration step, you will achieve superior stability and performance for your PHP web applications. Good luck with your project!

Share: