How to Install Apache, MariaDB and PHP on CentOS Stream 9

Install apache mariadb and php on centos stream 9 with secure PHP-FPM, firewalld, database security, service checks, and a working PHP test page.
A polished 3D server rack with a glowing web server, database cylinder, and PHP pipeline on a midnight-blue background with a subtle red accent.

To install Apache MariaDB and PHP on CentOS Stream 9, use the distribution packages, connect Apache to PHP-FPM, and keep MariaDB off the public network. A fresh VPS should be serving and executing a test PHP page in about 10–15 minutes.

Prerequisites

  • A fresh CentOS Stream 9 server with at least 1 GB RAM and 10 GB of free disk space for the web stack and application files.
  • SSH access as root, or a normal account with working sudo privileges.
  • A public IPv4 address if the site must be reachable from the internet; use SERVER_IP in the examples below.
  • A domain such as YOUR_DOMAIN is optional for this base installation, but required before you configure a trusted HTTPS certificate.
  • If you need the server first, choose a VPSLake Linux VPS with CentOS Stream 9 access.

This guide uses CentOS Stream 9’s AppStream packages instead of adding a third-party repository. The enabled stream determines the exact PHP and MariaDB point releases, while the commands and service names remain stable.

Step 1: Update CentOS Stream and install the stack

Updating metadata first prevents an old package cache from producing incomplete dependency or repository errors. This installs Apache, MariaDB, PHP-FPM, the MySQL-compatible PHP driver, and the firewall service in one pass.

sudo dnf update -y
sudo dnf install -y httpd firewalld mariadb-server mariadb php php-cli php-fpm php-mysqlnd php-gd php-mbstring php-opcache curl

Start each daemon and enable it for the next boot. PHP-FPM is the supported Apache integration on the EL9 family; it processes PHP separately from the web server.

sudo systemctl enable --now firewalld httpd mariadb php-fpm
sudo systemctl is-active firewalld httpd mariadb php-fpm

Expected output is one active line for each service:

active
active
active
active

The official Apache HTTP Server 2.4 documentation covers the same httpd configuration and service concepts used by CentOS Stream 9.

Step 2: Allow web traffic through firewalld

Apache cannot be reached from outside the VPS until the host firewall permits HTTP and HTTPS. Keep SSH allowed so a future firewall reload does not remove your remote administration path.

sudo firewall-cmd --permanent --zone=public --add-service=ssh
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --zone=public --list-services

The final command should include ssh, http, and https, although it may also list services already enabled by your VPS image:

cockpit dhcpv6-client http https ssh

Port 3306 is intentionally absent. PHP and MariaDB are on the same VPS, so the application can use the local socket; exposing the database to the internet creates an unnecessary attack surface.

Step 3: Confirm Apache before adding PHP code

Check Apache’s configuration and local HTTP response before debugging PHP. This separates a web-server or firewall issue from an application-runtime issue.

sudo apachectl configtest
curl -I http://127.0.0.1/

You should see Syntax OK followed by a successful HTTP response:

Syntax OK
HTTP/1.1 200 OK

The exact Server header and date vary. From another computer, open http://SERVER_IP/; if the default page loads locally but not remotely, recheck the firewalld rules and any provider-level network firewall.

Step 4: Secure and verify MariaDB

The hardening assistant removes default accounts and test data, and prevents remote root administration. Run it interactively because the available prompts differ slightly between MariaDB package releases.

sudo mariadb-secure-installation

When prompted, keep the secure choices: remove anonymous users, disallow remote root login, remove the test database, and reload the privilege tables. On current MariaDB packages, root may authenticate through the Unix socket instead of a password; do not force a password login if the script offers socket authentication and you do not need remote database administration. If you create a root password, use a long unique value represented by DB_ROOT_PASSWORD, never a reused VPS password.

Test the local client and record the installed server version:

sudo mariadb -e "SELECT VERSION();"
sudo mariadb -e "SHOW DATABASES;"

The first command prints one version row, and the second includes system databases such as information_schema, mysql, and performance_schema. The MariaDB secure-installation documentation explains why socket authentication and the cleanup questions can differ from older MySQL tutorials.

Step 5: Install and test PHP through PHP-FPM

The PHP packages provide the interpreter and common extensions, while PHP-FPM gives Apache a maintained FastCGI process to execute .php files. Restart Apache after the packages are installed so it loads the PHP-FPM configuration.

php -v
php -m | grep -E 'mysqli|mysqlnd|PDO|pdo_mysql'
sudo systemctl restart php-fpm httpd

The version is determined by the enabled CentOS Stream 9 repository. The module check should include at least mysqli, mysqlnd, PDO, and pdo_mysql.

Create a short-lived test file that prints a harmless message rather than exposing the full phpinfo() configuration page.

echo '<?php echo "PHP is working\n"; ?>' | sudo tee /var/www/html/info.php >/dev/null
curl -fsS http://127.0.0.1/info.php
sudo rm -f /var/www/html/info.php

Expected output:

PHP is working

Removing info.php immediately matters because diagnostic pages can disclose paths, loaded modules, and environment variables. Leave /var/www/html with only your real site files.

Verify the LAMP stack

Run the final checks together to prove that all three layers are ready: Apache answers HTTP, PHP-FPM is active and executes code, and MariaDB accepts a local administrative query.

sudo systemctl is-active httpd php-fpm mariadb
curl -fsS http://127.0.0.1/
sudo mariadb -e "SELECT VERSION() AS mariadb_version;"

The service check should return three active lines. The curl command should return the Apache welcome page or your own index content, and the SQL query should return a mariadb_version column with a version value. Before deploying a login-capable site, point YOUR_DOMAIN to the VPS, configure HTTPS, and keep port 3306 closed unless a separate host genuinely needs database access.

Troubleshooting

Job for httpd.service failed or Syntax error

Apache found a malformed configuration file, often a leftover virtual-host file or a typo in a PHP-FPM handler. Read the exact filename from the configuration test, fix that file, and only then restart the service.

sudo apachectl configtest
sudo journalctl -u httpd -n 40 --no-pager
sudo systemctl restart httpd

curl works locally but the site times out remotely

The service is listening, but traffic is being blocked by firewalld or by the VPS provider’s external firewall. Confirm the public-zone rules and reapply them without opening MariaDB.

sudo firewall-cmd --zone=public --list-services
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload
sudo ss -ltnp | grep -E ':(80|443)\b'

If the local check succeeds and port 80 is listening, allow TCP 80 and 443 in the VPS control panel’s network firewall as well.

The browser downloads PHP or shows PHP source code

Apache is serving the file as static text because PHP-FPM is stopped or its Apache handler was not loaded. Check the daemon, PHP configuration, and recent Apache messages, then restart both services.

sudo systemctl status php-fpm --no-pager
sudo test -f /etc/httpd/conf.d/php.conf && echo 'PHP-FPM Apache config found'
sudo journalctl -u php-fpm -n 40 --no-pager
sudo systemctl restart php-fpm httpd

Do not work around this by enabling an old third-party mod_php package; keep the EL9 PHP-FPM path and fix the service or configuration error.

ERROR 1698 (28000): Access denied for user 'root'@'localhost'

That usually means MariaDB is using Unix-socket authentication, which is a normal secure default. Run administrative commands through sudo mariadb; use -p only if you deliberately configured a password-authenticated root account.

sudo mariadb -e "SELECT USER(), CURRENT_USER();"

Apache returns 403 Forbidden for a new document

Files copied from another directory can have an incorrect SELinux context even when their Unix permissions look right. Restore the default web-root labels instead of disabling SELinux.

sudo restorecon -Rv /var/www/html
sudo find /var/www/html -type d -exec chmod 0755 {} \;
sudo find /var/www/html -type f -exec chmod 0644 {} \;

The web root should be readable by Apache and should not be world-writable. If the denial persists, inspect sudo ausearch -m AVC -ts recent and correct the named path.

Hardening

  • Install a trusted certificate for YOUR_DOMAIN and redirect HTTP to HTTPS before deploying an application that accepts passwords, payments, or personal data.
  • Keep MariaDB bound to localhost unless a separate application host requires access; create a limited database user instead of using root in application configuration.
  • Replace the default Apache page with a site-specific virtual host and disable directory listing with Options -Indexes where appropriate.
  • Review enabled ports with sudo firewall-cmd --zone=public --list-all after each service installation.

FAQ

Is MariaDB compatible with MySQL applications?

MariaDB implements the MySQL client protocol and supports the common SQL features used by many PHP applications. Check the application’s compatibility matrix before relying on a MySQL-specific feature or version requirement.

Do I need to open port 3306 for PHP?

No. When PHP and MariaDB share this VPS, the application can connect through the local socket or loopback interface. Open 3306 only for a documented remote client, and restrict the source address rather than allowing the whole internet.

Which PHP version does CentOS Stream 9 install?

The enabled AppStream repository decides the version available to your image, so inspect it with php -v after installation. This guide intentionally avoids pinning a version that may change during the Stream 9 lifecycle.

Can I install a website before configuring HTTPS?

You can use HTTP for the local smoke test and initial virtual-host setup. Configure HTTPS before exposing an account, admin panel, checkout page, or any other site that handles credentials or sensitive data.

Samrat Ghosh
Written by

Samrat Ghosh

Founder & Infrastructure Engineer

Samrat Ghosh is the founder of VPSLake, where he builds and runs the remote desktop and VPS hosting infrastructure the company is built on. He writes hands-on guides about RDP, Windows Server, VPS management and secure remote access - the practical documentation he wishes had…

Previous Article

5 Best DigitalOcean Alternatives in 2026

Write a Comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Subscribe to our Newsletter

Subscribe to our email newsletter to get the latest posts delivered right to your email.
Pure inspiration, zero spam ✨