A panel-free installation shows you exactly what is happening on the server and leaves the resources a panel would consume to your application. For a single site, this route is lighter and more instructive.
If you plan to host several sites or open accounts for customers, a panel makes more sense; see our Plesk installation guide.
Preparation
We assume an empty Ubuntu 24.04 server. Before starting, update the system and confirm the domain points at it:
bashapt update && apt upgrade -y
dig +short yourdomain.comThe second command should return your server's IP.
1. Nginx, MariaDB and PHP
bashapt install -y nginx mariadb-server php-fpm php-mysql php-curl php-gd php-mbstring php-xml php-zip php-imagickSecure the database server:
bashmysql_secure_installationThe script asks about a root password, removing anonymous users and dropping the test database. Answer yes to all.
2. Create a database and user
bashmysql -u root -ptextCREATE DATABASE wp_database DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'a-strong-password';
GRANT ALL PRIVILEGES ON wp_database.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;Do not use the root user for WordPress. Give the application a dedicated user that can reach only its own database; if something is compromised, the damage stays there.
The utf8mb4 character set matters: it is what stores accented characters and emoji correctly.
3. Download WordPress
bashcd /tmp
curl -O https://wordpress.org/latest.tar.gz
tar xzf latest.tar.gz
mkdir -p /var/www/yourdomain.com
cp -r wordpress/* /var/www/yourdomain.com/4. File permissions
This is where the balance between security and usability is set:
bashchown -R www-data:www-data /var/www/yourdomain.com
find /var/www/yourdomain.com -type d -exec chmod 755 {} \;
find /var/www/yourdomain.com -type f -exec chmod 644 {} \;755 for directories, 644 for files. Do not use 777 — that widely repeated advice means any process on the server can modify your files.
5. The Nginx server block
Create /etc/nginx/sites-available/yourdomain.com:
textserver {
listen 80;
server_name yourdomain.com www.yourdomain.com;
root /var/www/yourdomain.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 ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
}That last block matters: it stops a PHP file that slips into the uploads directory from executing. It is one of the standard routes into a WordPress install.
client_max_body_size sets the media upload limit; the 1 MB default is too small for most sites.
Enable and test:
bashln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx6. Finish the install
Open your domain in a browser. WordPress takes you to the setup wizard; enter the database details from step 2.
Do not use `admin` as the administrator username. It is the first thing automated attacks try.
7. SSL
bashapt install -y certbot python3-certbot-nginx
certbot --nginx -d yourdomain.com -d www.yourdomain.comAfter the certificate is installed, update the site address in WordPress settings to https:// too, otherwise you will get mixed content warnings.
After installation
- Set up automated backups with a destination outside the server
- Disable XML-RPC if you do not use it; it is a common brute-force target
- Turn off the file editor: add
define('DISALLOW_FILE_EDIT', true);towp-config.php - For server-side hardening see our server security guide
- For speed, read our WordPress optimization guide
If you would rather we handled the installation, we run setup and migration remotely.
