PHP and MySQL power WordPress, Laravel, Drupal and countless other applications. This guide installs a modern PHP-FPM and a MySQL-compatible database on Ubuntu or Debian, connected to Nginx. (Prefer Apache? See the LAMP stack guide.)

1. Install PHP-FPM and extensions

apt update
apt install -y php-fpm php-mysql php-cli php-curl php-gd php-mbstring php-xml \
               php-zip php-intl php-bcmath php-opcache
php -v

This installs the PHP version that ships with your release (e.g. PHP 8.3 on Ubuntu 24.04, PHP 8.2 on Debian 12, PHP 8.4 on Debian 13). If an application needs a different version, the well-known Ondřej Surý repository provides current PHP versions for Ubuntu (PPA) and Debian (packages.sury.org).

Check the FPM service:

systemctl status php*-fpm
ls /run/php/          # the socket, e.g. php8.3-fpm.sock

2. Install the database

Debian and Ubuntu use MariaDB as their MySQL-compatible server:

apt install -y mariadb-server
mariadb-secure-installation

If you specifically need Oracle MySQL (for example, an application certified only on MySQL 8), Ubuntu offers mysql-server; on Debian, use MySQL’s official APT repository. For most PHP applications, MariaDB works identically. Details: installing MariaDB.

3. Create a database and user

mariadb
CREATE DATABASE app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'app'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON app.* TO 'app'@'localhost';
FLUSH PRIVILEGES;
EXIT;

4. Connect Nginx to PHP-FPM

apt install -y nginx

/etc/nginx/sites-available/example.com:

server {
    listen 80;
    server_name example.com;
    root /var/www/example.com;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;   # match your version
    }

    location ~ /\.ht { deny all; }
}
mkdir -p /var/www/example.com
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

5. Test PHP and the database connection

cat > /var/www/example.com/db-test.php <<'EOF'
<?php
$pdo = new PDO('mysql:host=localhost;dbname=app;charset=utf8mb4', 'app', 'a-long-random-password');
echo 'PHP ' . PHP_VERSION . ' connected to ' . $pdo->query('SELECT VERSION()')->fetchColumn();
EOF
curl http://example.com/db-test.php
rm /var/www/example.com/db-test.php       # never leave test files online

6. Tune php.ini

Edit /etc/php/8.3/fpm/php.ini (adjust the version):

memory_limit = 256M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 60
expose_php = Off
date.timezone = UTC

Make sure OPcache is enabled (php -i | grep opcache.enable). Reload: systemctl reload php8.3-fpm. For FPM worker sizing, see WordPress VPS performance tuning.

7. HTTPS

apt install -y certbot python3-certbot-nginx
certbot --nginx -d example.com --redirect

8. Composer (for frameworks)

apt install -y composer
composer --version

Run Composer as the site user, not root.

Troubleshooting

Problem Fix
PHP file downloads instead of running The location ~ \.php$ block is missing or the socket path is wrong
502 Bad Gateway PHP-FPM not running, or socket name doesn’t match the installed version
could not find driver php-mysql not installed for the FPM version in use; reload FPM
Access denied for user Wrong password or the user was created for a different host

Related: hosting multiple websites on one VPS and our WordPress VPS page.

  • PHP
  • MySQL
  • MariaDB
  • Nginx