Running WordPress on your own VPS gives you full control over performance, PHP versions and security — and costs less than managed WordPress hosting once you have more than one site. This guide sets up a fast, clean LEMP stack (Linux, Nginx, MariaDB, PHP-FPM) on Ubuntu 24.04 and installs WordPress on it.

You need a fresh Ubuntu server with a sudo user, and a domain whose A record points to the server.

1. Install Nginx, MariaDB and PHP

sudo apt update
sudo apt install -y nginx mariadb-server \
  php-fpm php-mysql php-curl php-gd php-intl php-mbstring php-soap \
  php-xml php-xmlrpc php-zip php-imagick php-bcmath

Check the PHP version (you’ll need it for the socket path):

php -v        # e.g. PHP 8.3

Secure MariaDB:

sudo mariadb-secure-installation

Answer yes to removing anonymous users, disallowing remote root login and removing the test database. Root keeps socket authentication (sudo mariadb).

2. Create the database

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

3. Download WordPress

cd /tmp
curl -LO https://wordpress.org/latest.tar.gz
tar xzf latest.tar.gz
sudo mkdir -p /var/www/example.com
sudo cp -a wordpress/. /var/www/example.com/
sudo chown -R www-data:www-data /var/www/example.com

4. Configure wp-config.php

cd /var/www/example.com
sudo -u www-data cp wp-config-sample.php wp-config.php
sudo -u www-data nano wp-config.php

Set the database details:

define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wpuser' );
define( 'DB_PASSWORD', 'a-long-random-password' );
define( 'DB_HOST', 'localhost' );

Replace the placeholder salts with fresh ones from https://api.wordpress.org/secret-key/1.1/salt/. Then tighten permissions:

sudo chmod 640 wp-config.php

5. Nginx server block

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

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    root /var/www/example.com;
    index index.php;

    client_max_body_size 64M;

    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 ~* \.(css|js|jpg|jpeg|png|gif|webp|avif|svg|ico|woff2)$ {
        expires 30d;
        access_log off;
    }

    location = /xmlrpc.php { deny all; }
    location ~ /\.(?!well-known) { deny all; }
    location ~* /uploads/.*\.php$ { deny all; }
}
  • try_files … /index.php?$args makes pretty permalinks work.
  • The last three blocks block XML-RPC (a common brute-force target — remove that line if you use Jetpack or the mobile app), hidden files, and PHP execution in uploads.
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

More options in WordPress Nginx configuration.

6. PHP settings

In /etc/php/8.3/fpm/php.ini:

upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
max_execution_time = 120
sudo systemctl restart php8.3-fpm

7. HTTPS

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

8. Run the installer

Open https://example.com and complete the five-minute install: site title, admin username (not “admin”), a strong password and your email.

Or with WP-CLI:

sudo -u www-data wp core install --url=https://example.com --title="My Site" \
  --admin_user=yourname [email protected] --prompt=admin_password

9. Post-install essentials

  • Permalinks: Settings → Permalinks → “Post name”.
  • Real cron: disable WP-Cron’s page-load trigger and run it from system cron — see WordPress real cron.
  • Object cache: install Redis and a Redis object-cache plugin — see WordPress Redis object cache.
  • Page cache: a caching plugin or Nginx FastCGI cache makes most pages load without touching PHP.
  • Backups: files plus database, copied off the server.
  • Security: see WordPress hardening.

File permissions

The setup above makes www-data own all files, which lets WordPress install plugins and updates itself. For stricter security, have a deploy user own the code and let www-data write only to wp-content/uploads — then updates go through WP-CLI. See WordPress file permissions.

Troubleshooting

  • “Error establishing a database connection” — wrong credentials in wp-config.php, or MariaDB isn’t running. See the fix.
  • 502 Bad Gateway — PHP-FPM isn’t running or the socket path in Nginx doesn’t match: ls /run/php/.
  • Permalinks give 404 — the try_files line is missing.
  • Upload size errors — raise both the PHP limits and client_max_body_size.

Want a ready-made WordPress server instead? Our WordPress VPS page explains plan sizing for one or many sites.

  • WordPress
  • Nginx
  • Ubuntu