WordPress runs well on Ubuntu 24.04 with Nginx, PHP-FPM, and MariaDB when you want full control over the web server, PHP runtime, database, files, TLS, and backups. In this tutorial, you will install WordPress on a Raff Linux VM, configure the Nginx WordPress server block, keep MariaDB local to the server, protect wp-config.php, enable HTTPS with Let's Encrypt, and verify the finished site end to end.
Ubuntu 24.04's PHP 8.3 baseline matches WordPress's current recommended PHP requirement, and the MariaDB version available on Ubuntu 24.04 meets WordPress's modern database baseline. This guide uses a single-server WordPress VPS design: Nginx, PHP-FPM, MariaDB, and WordPress all run on the same VM. For a small site, start with at least 2 GB RAM; use more memory when plugins, backups, imports, WooCommerce, or traffic increase PHP and database load.
You need a domain whose DNS can point to the VM, SSH access with a non-root sudo user, and a fresh or maintained Ubuntu 24.04 server. Keep the domain placeholder example.com until you replace it with your real hostname.
Step 1 — Confirm DNS and Update Ubuntu
Point the domain's A record to the public IPv4 address of your Raff Linux VM. Only add an AAAA record when IPv6 is configured and reachable on the same server.
From your workstation, check DNS:
dig +short A example.com dig +short AAAA example.com
Then connect over SSH and update Ubuntu:
sudo apt update sudo apt upgrade -y
If the upgrade reports that a reboot is required, reboot before continuing and reconnect over SSH:
sudo reboot
Verify: dig +short A example.com should return the VM's public IPv4 address, and sudo apt update should complete without repository errors.
Step 2 — Install Nginx, MariaDB, PHP-FPM, and WordPress Extensions
Install Nginx, MariaDB, PHP-FPM, and the PHP extensions commonly needed by WordPress:
sudo apt install -y \ nginx \ mariadb-server \ php-fpm \ php-mysql \ php-curl \ php-gd \ php-intl \ php-mbstring \ php-soap \ php-xml \ php-zip \ php-imagick \ curl \ tar \ openssl
Enable the core services:
sudo systemctl enable --now nginx mariadb php8.3-fpm
Check the installed versions:
nginx -v php -v mariadb --version
WordPress currently recommends PHP 8.3 or newer, MariaDB 10.11 or newer or MySQL 8.0 or newer, plus HTTPS. Ubuntu 24.04's standard PHP-FPM service is php8.3-fpm; if you deliberately installed another PHP version, use that service and socket consistently in later steps.
Verify: Run:
systemctl is-active nginx mariadb php8.3-fpm
All three services should return active.
Step 3 — Secure MariaDB Before Creating WordPress Data
Run MariaDB's security helper:
sudo mariadb-secure-installation
For a local single-server WordPress deployment, keep local administrative access protected, remove anonymous users, remove the test database, and disallow unnecessary remote root access. WordPress will use its own database user rather than the MariaDB root account.
Check that MariaDB is listening locally:
sudo ss -ltnp | grep 3306 || true
Do not open TCP port 3306 in UFW or another public firewall for this architecture.
Verify: sudo mariadb -e "SELECT VERSION();" should return the database version, and there should be no public firewall rule exposing port 3306.
Step 4 — Create a Dedicated WordPress Database and User
Generate a strong database password and save it in your password manager:
openssl rand -hex 24
Open the MariaDB shell:
sudo mariadb
Create the database and a local-only user. Replace use_a_unique_password with the password you generated:
CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'use_a_unique_password'; GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost'; FLUSH PRIVILEGES; EXIT;
Test the WordPress account:
mariadb -u wpuser -p wordpress
Then run:
SELECT DATABASE(), CURRENT_USER(); EXIT;
Verify: The query should show wordpress as the active database and wpuser@localhost as the current user.
Step 5 — Download WordPress from the Official Source
Download the current WordPress archive from WordPress.org:
cd /tmp curl -fLO https://wordpress.org/latest.tar.gz
Extract it:
tar -xzf latest.tar.gz
Create the site directory and copy the WordPress files:
sudo mkdir -p /var/www/example.com/public sudo cp -a /tmp/wordpress/. /var/www/example.com/public/
Confirm the expected core files exist:
ls -l /var/www/example.com/public/wp-admin/index.php ls -l /var/www/example.com/public/wp-includes/version.php
You can inspect the downloaded WordPress version with:
grep -m1 '\$wp_version =' /var/www/example.com/public/wp-includes/version.php
Verify: wp-admin/index.php and wp-includes/version.php should both exist, and the version file should contain a WordPress version value.
Step 6 — Configure wp-config.php and Authentication Salts
Create wp-config.php from the sample:
sudo cp /var/www/example.com/public/wp-config-sample.php \ /var/www/example.com/public/wp-config.php
Open the file:
sudo nano /var/www/example.com/public/wp-config.php
Set the database values created in Step 4:
define( 'DB_NAME', 'wordpress' ); define( 'DB_USER', 'wpuser' ); define( 'DB_PASSWORD', 'use_a_unique_password' ); define( 'DB_HOST', 'localhost' ); define( 'DB_CHARSET', 'utf8mb4' );
Generate fresh WordPress authentication keys and salts from the official API:
curl -s https://api.wordpress.org/secret-key/1.1/salt/
Replace the placeholder authentication key and salt definitions in wp-config.php with the generated values.
Disable the built-in plugin and theme file editors by adding this above the final stop-editing comment:
define( 'DISALLOW_FILE_EDIT', true );
This removes a common code-editing path from the WordPress dashboard if an administrator account is compromised.
Verify: Confirm the database constants exist without printing the password to your terminal history again:
sudo grep -E "DB_NAME|DB_USER|DB_HOST|DISALLOW_FILE_EDIT" \ /var/www/example.com/public/wp-config.php
You should see the expected database name, user, host, and DISALLOW_FILE_EDIT setting.
Step 7 — Apply Safer WordPress Ownership and File Permissions
WordPress's hardening guidance recommends keeping core files writable by the administrative user rather than making every application file writable by the web server. Use your SSH user as the owner and www-data as the group:
sudo chown -R "$USER":www-data /var/www/example.com
Set directories to 750 and files to 640:
sudo find /var/www/example.com -type d -exec chmod 750 {} \; sudo find /var/www/example.com -type f -exec chmod 640 {} \;
Allow PHP-FPM to write inside wp-content, where uploads, caches, and some plugin operations need write access:
sudo find /var/www/example.com/public/wp-content -type d -exec chmod 770 {} \; sudo find /var/www/example.com/public/wp-content -type f -exec chmod 660 {} \;
Keep wp-config.php restricted:
sudo chmod 640 /var/www/example.com/public/wp-config.php
Do not use chmod 777 on WordPress files or directories. This layout intentionally keeps the WordPress core less writable than wp-content; if a dashboard-based core update cannot replace core files, perform the update through your normal administrative deployment workflow instead of loosening the entire document root.
Verify: Run:
stat -c '%U %G %a %n' \ /var/www/example.com/public/wp-config.php \ /var/www/example.com/public/wp-content
The owner should be your Linux user, the group should be www-data, and wp-content should be writable by the group while wp-config.php remains restricted.
Step 8 — Configure Nginx for WordPress and Block PHP in Uploads
Create the Nginx server block:
sudo nano /etc/nginx/sites-available/example.com
Add:
server { listen 80; listen [::]:80; server_name example.com www.example.com; root /var/www/example.com/public; index index.php index.html; client_max_body_size 64M; access_log /var/log/nginx/example.com.access.log; error_log /var/log/nginx/example.com.error.log; location / { try_files $uri $uri/ /index.php?$args; } location = /wp-config.php { deny all; } location ~* /(?:uploads|files)/.*\.php$ { deny all; } location ~ /\.(?!well-known).* { deny all; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.3-fpm.sock; } location = /xmlrpc.php { deny all; } location ~* \.(css|gif|ico|jpeg|jpg|js|png|svg|webp|woff|woff2)$ { expires 7d; access_log off; } }
The try_files rule supports WordPress pretty permalinks. The uploads restriction prevents a .php file placed under common upload paths from being executed by PHP-FPM. The xmlrpc.php block is optional: remove it if you use Jetpack, the WordPress mobile app, remote publishing, or another XML-RPC-dependent integration.
Enable the site and remove the default site symlink:
sudo ln -s /etc/nginx/sites-available/example.com \ /etc/nginx/sites-enabled/example.com sudo rm -f /etc/nginx/sites-enabled/default
Test and reload Nginx:
sudo nginx -t sudo systemctl reload nginx
Verify: sudo nginx -t should report that the configuration syntax is successful, and curl -I http://127.0.0.1 -H 'Host: example.com' should return an HTTP response from the new server block.
Step 9 — Configure UFW Without Locking Out SSH
Check UFW first:
sudo ufw status
If UFW is active, allow the existing SSH path before changing web rules:
sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw status
Do not open MariaDB port 3306 publicly for this single-VM WordPress setup.
If UFW is currently inactive, review your SSH access and firewall policy before enabling it. Do not blindly enable a firewall on a remote server without confirming that your management path is allowed.
Verify: When UFW is active, the status should allow SSH plus HTTP/HTTPS and should not contain a public MariaDB rule.
Step 10 — Enable HTTPS with Let's Encrypt
Install Certbot and its Nginx plugin:
sudo apt install -y certbot python3-certbot-nginx
Request a certificate and configure an HTTP-to-HTTPS redirect:
sudo certbot --nginx --redirect \ -d example.com \ -d www.example.com
Only request hostnames whose public DNS records already point to this VM.
Check the certificate and renewal path:
sudo certbot certificates sudo certbot renew --dry-run
For deeper certificate troubleshooting, use How to Secure Nginx with Let's Encrypt on Ubuntu 24.04.
Verify: Open https://example.com or run:
curl -I https://example.com
The connection should complete over HTTPS, and an HTTP request should redirect to HTTPS after Certbot configures the site.
Step 11 — Complete the WordPress Browser Installation
Open:
https://example.com
Choose the site language and enter:
- Site title
- Administrator username
- Strong administrator password
- Administrator email address
- Search-engine visibility preference
Avoid predictable administrator usernames such as admin. Complete the installer, then sign in at:
https://example.com/wp-admin/
After login, immediately review Dashboard → Updates and apply required WordPress, theme, and plugin updates using an update method compatible with the file-permission model from Step 7.
Verify: You should be able to sign in to /wp-admin/, open the site homepage in another browser window, and load a WordPress page over HTTPS without a certificate warning.