Quick answer: Install Nginx, MariaDB and PHP-FPM, create a database for WordPress, extract WordPress to /var/www/wordpress, add an Nginx server block that passes .php requests to the PHP-FPM socket, then finish the install in your browser. Add a free SSL certificate with Certbot before you go live.
This guide is written for Ubuntu 24.04 LTS, which ships PHP 8.3 by default. The same steps work on Ubuntu 22.04 and on Debian, with only the PHP version number changing.
A note if you followed the old version of this guide: it used PHP 5, which reached end of life at the end of 2018 and has received no security patches since. If you are still running PHP 5 anywhere, that is the most urgent thing on your to-do list. The steps below replace it entirely.
Before you begin
- A server running Ubuntu 24.04 or 22.04, with a user who has
sudorights. - A domain name pointed at the server’s IP address, if you intend to use SSL.
- SSH access to the server.
Start by updating the system:
sudo apt update && sudo apt upgrade -y
Step 1: Install Nginx
sudo apt install nginx -y
Enable it so it starts automatically after a reboot, and start it now:
sudo systemctl enable nginx
sudo systemctl start nginx
If the firewall is active, allow web traffic through:
sudo ufw allow 'Nginx Full'
Visit your server’s IP address in a browser. The default Nginx welcome page confirms it is working.
Step 2: Install MariaDB and secure it
sudo apt install mariadb-server -y
sudo systemctl enable --now mariadb
Run the hardening script, which removes the test database, anonymous users and remote root login:
sudo mysql_secure_installation
Answer the prompts as follows:
| Prompt | Answer | Why |
|---|---|---|
| Enter current password for root | Press Enter | None is set yet |
| Switch to unix_socket authentication | Y | More secure than a password for local root |
| Change the root password | N (if you chose unix_socket) | Not needed with socket auth |
| Remove anonymous users | Y | They allow unauthenticated access |
| Disallow root login remotely | Y | Root should only connect locally |
| Remove test database | Y | It is world-writable by default |
| Reload privilege tables | Y | Applies everything immediately |
Step 3: Create the WordPress database
With unix_socket authentication, you log in as root without a password:
sudo mysql
Then create the database and a dedicated user. Replace the password with a strong one of your own:
CREATE DATABASE wpdb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'your_strong_password_here';
GRANT ALL PRIVILEGES ON wpdb.* TO 'wpuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Using utf8mb4 matters. It is what lets WordPress store emoji and the full range of Unicode characters without mangling them.
Never let WordPress connect as the database root user. A dedicated account limited to one database means a compromised site cannot reach anything else on the server.
Step 4: Install PHP-FPM and the modules WordPress needs
sudo apt install php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zip php-intl php-imagick -y
On Ubuntu 24.04 this installs PHP 8.3. Confirm the version and that the service is running:
php -v
systemctl status php8.3-fpm
Note the version number, because you need it for the socket path in the Nginx configuration below.
Step 5: Download WordPress
cd /tmp
wget https://wordpress.org/latest.tar.gz
tar -xvzf latest.tar.gz
sudo mv wordpress /var/www/wordpress
Installing to /var/www/wordpress rather than /var/www/html keeps your site separate from the Nginx default, which makes hosting more than one site far tidier later.
Step 6: Set ownership and permissions
Get this right and a great many “WordPress cannot write to the directory” problems never happen.
sudo chown -R www-data:www-data /var/www/wordpress
sudo find /var/www/wordpress -type d -exec chmod 755 {} \;
sudo find /var/www/wordpress -type f -exec chmod 644 {} \;
Directories get 755 and files get 644. Applying 755 to everything, as older guides suggest, makes every file executable, which is unnecessary and a little reckless.
Step 7: Create the Nginx server block
Create a configuration file for your site:
sudo nano /etc/nginx/sites-available/wordpress
Paste the following, replacing example.com with your domain and checking the PHP version in the socket path:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/wordpress;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
location ~ /\.ht {
deny all;
}
location = /favicon.ico { log_not_found off; access_log off; }
location = /robots.txt { allow all; log_not_found off; access_log off; }
}
The try_files $uri $uri/ /index.php?$args; line is what makes WordPress permalinks work. Without it, every page except the homepage returns a 404.
Enable the site, remove the default, test the configuration and reload:
sudo ln -s /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
Always run nginx -t before reloading. It catches syntax errors while the old configuration is still serving traffic.
Step 8: Configure wp-config.php
sudo cp /var/www/wordpress/wp-config-sample.php /var/www/wordpress/wp-config.php
sudo nano /var/www/wordpress/wp-config.php
Fill in the database details you created earlier:
define( 'DB_NAME', 'wpdb' );
define( 'DB_USER', 'wpuser' );
define( 'DB_PASSWORD', 'your_strong_password_here' );
define( 'DB_HOST', 'localhost' );
Then replace the authentication keys and salts. Visit the WordPress secret key generator, copy the block it produces, and paste it over the matching placeholder lines. Skipping this step leaves your sessions far easier to hijack.
Step 9: Finish the install in your browser
Go to your domain or server IP. The WordPress setup screen appears. Choose your site title, create an admin account with a strong password, and complete the installation.
Pick something other than admin for the username. Automated attacks try that first, every time.
Step 10: Add a free SSL certificate
Do not leave a WordPress login form running over plain HTTP.
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.com -d www.example.com
Certbot obtains the certificate, edits your server block to serve HTTPS, and sets up automatic renewal. Confirm renewal works with:
sudo certbot renew --dry-run
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 502 Bad Gateway | Wrong PHP-FPM socket path | Check the version with php -v and match the socket in the server block |
| Browser downloads the PHP file | Nginx is not passing PHP to FPM | Confirm the location ~ \.php$ block exists and reload Nginx |
| Homepage works, other pages 404 | Missing try_files rule | Add try_files $uri $uri/ /index.php?$args; |
| “Error establishing a database connection” | Wrong credentials in wp-config.php | Recheck the name, user and password, and that MariaDB is running |
| Cannot upload media or install plugins | Ownership is wrong | Run sudo chown -R www-data:www-data /var/www/wordpress |
| Upload size too small | PHP defaults are conservative | Raise upload_max_filesize and post_max_size in /etc/php/8.3/fpm/php.ini, then restart PHP-FPM |
| Certbot cannot verify the domain | DNS not pointing at the server | Confirm the A record resolves to your server IP and port 80 is open |
Frequently asked questions
Nginx or Apache for WordPress?
Both work well. Nginx typically handles many simultaneous connections with less memory, which matters on small servers. Apache is more forgiving because of .htaccess support. On a modest VPS, Nginx is the better default.
MariaDB or MySQL?
MariaDB is a drop-in replacement and is what Ubuntu packages by default. WordPress cannot tell the difference.
Do I have to use PHP 8.3?
Use whatever your Ubuntu release ships, so long as it is still supported. Avoid anything below PHP 8.1, since older versions no longer receive security fixes.
How do I host a second site?
Create another directory under /var/www, add a second server block with its own server_name, and enable it. That is exactly why this guide avoids /var/www/html.
Should I still run apt-get?
Use apt. It is the modern front-end and the one Ubuntu recommends for interactive use.
Related guides
- Install Nginx, MariaDB and PHP on Ubuntu (LEMP Stack), if you want the stack on its own without WordPress on top.
- Install VirtualBox on a Headless Ubuntu Server, useful for rehearsing a server build in a VM before touching production.