What you need
A fresh Ubuntu LTS or Debian server, ideally Small (2 vCPU, 4 GB) or larger, a domain pointing at it, and the basics from our security checklist already done. Open ports 80 and 443.
1. Install the stack
sudo apt update
sudo apt install -y nginx mariadb-server php-fpm php-mysql php-curl php-gd php-intl \
php-mbstring php-xml php-zip php-imagick certbot python3-certbot-nginx
php -v
Note the PHP version, for example 8.3; it appears in file paths below.
2. Create the database
sudo mariadb-secure-installation
sudo mariadb <<'EOF'
CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp'@'localhost' IDENTIFIED BY 'use-a-long-random-password';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wp'@'localhost';
FLUSH PRIVILEGES;
EOF
MariaDB listens on localhost only by default; keep it that way.
3. Download WordPress
cd /tmp && curl -LO https://wordpress.org/latest.tar.gz
sudo mkdir -p /var/www/example.com
sudo tar xzf latest.tar.gz -C /var/www/example.com --strip-components=1
sudo chown -R www-data:www-data /var/www/example.com
4. Configure nginx
Create /etc/nginx/sites-available/example.com:
server {
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|svg|woff2)$ {
expires 30d;
access_log off;
}
location = /xmlrpc.php { deny all; }
location ~ /\.(?!well-known) { deny all; }
}
Enable it and request a certificate:
sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d example.com -d www.example.com --redirect
Blocking xmlrpc.php stops a common brute-force route; remove that line if you use the Jetpack plugin or the mobile app.
5. Tune PHP-FPM for 4 GB
Edit /etc/php/8.3/fpm/pool.d/www.conf:
pm = dynamic
pm.max_children = 12
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
Each PHP worker typically uses 40 to 80 MB, so twelve children leave room for MariaDB and the page cache. In /etc/php/8.3/fpm/php.ini set upload_max_filesize = 64M, post_max_size = 64M and memory_limit = 256M, then sudo systemctl restart php8.3-fpm.
6. Finish the install
Visit https://example.com, choose a language, and enter the database name, user and password from step 2. Use a unique admin username rather than admin.
7. Speed and safety
- Install a page-caching plugin, or add Redis (
sudo apt install redis-server php-redis) with an object-cache plugin for dynamic sites. - Replace WP-Cron with a real cron job: add
define('DISABLE_WP_CRON', true);towp-config.phpand run*/5 * * * * curl -s https://example.com/wp-cron.php >/dev/nullfrom the server's crontab. - Keep plugins few and updated, and turn on automatic security updates for the OS.
- Take a snapshot before major WordPress or plugin upgrades, and keep scheduled backups of files and the database.
