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.