How to Install WordPress on Ubuntu Server (Current Guide)
Most WordPress installation guides online are version-specific and go stale fast. This guide is written to stay current: rather than hardcoding package versions that will be outdated within months, it shows you how to verify the latest stable versions at install time and install them accordingly. Commands are written for Ubuntu Server LTS (24.04) using Nginx as the primary web server, with a brief Apache section at the end. Ubuntu 25.10 is the current non-LTS release but is not recommended for a production server — its support window ends July 2026. Ubuntu 24.04 LTS is the correct choice for a new WordPress server.
At the time of writing (March 2026), the current stable versions are WordPress 6.9.4, PHP 8.5, MariaDB 11.8 LTS, and Nginx 1.28.x stable. Always verify current versions before installing — links to authoritative version sources are provided throughout.
Prerequisites
WordPress is lean enough to run on a Raspberry Pi — but for production business applications we’ll focus on cloud hardware that can handle real traffic, scale on demand, and stay online reliably.
- A fresh or existing Ubuntu Server 24.04 LTS instance — this guide was written and tested on Linode (Akamai Cloud). Their $6/month Nanode is sufficient for a small WordPress backend. (Referral link — I receive a small credit if you sign up.) For EU-based hosting with data residency in Europe, Hetzner is a well-regarded alternative with competitive pricing. AWS and Google Cloud also offer Ubuntu Server instances if you are already embedded in either ecosystem — the instructions in this guide apply equally to all providers.
- A non-root user with
sudoprivileges - A domain name pointed at your server’s IP (for SSL)
- SSH access
Step 1: Update the Server
Always start with a full package update:
sudo apt update && sudo apt upgrade -y
Step 2: Install Nginx
Nginx is available in Ubuntu’s default repositories. The version there may lag slightly behind the official Nginx stable release — for most WordPress installations the Ubuntu package is sufficient.
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx
Verify the installed version:
nginx -v
Check the current stable release: nginx.org/en/download.html
If you need the latest stable Nginx, add the official Nginx repository before installing:
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/ubuntu $(lsb_release -cs) nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt update && sudo apt install nginx -y
Allow Nginx and SSH through the firewall:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status
Always allow SSH before enabling UFW — enabling the firewall without it will lock you out of the server.
Verify Nginx is running by visiting your server’s IP in a browser — you should see the default Nginx welcome page.
Step 3: Install PHP
WordPress requires PHP 7.4 or higher; PHP 8.2+ is strongly recommended. PHP 8.4 and 8.5 are the current actively supported branches as of 2026.
Check the current stable PHP release: php.net/releases
Check PHP end-of-life dates: endoflife.date/php
Ubuntu 24.04 ships PHP 8.3 in its default repositories. To install PHP 8.4 or 8.5, add the Ondřej Surý PPA, which is the standard source for current PHP packages on Ubuntu:
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update
Install PHP 8.5 (or substitute 8.4 if you prefer the previous stable branch) with the extensions WordPress requires:
sudo apt install php8.5-fpm php8.5-mysql php8.5-curl php8.5-gd \
php8.5-mbstring php8.5-xml php8.5-xmlrpc php8.5-zip \
php8.5-imagick php8.5-intl php8.5-bcmath -y
Verify PHP installation:
php8.5 --version
systemctl status php8.5-fpm
Recommended PHP-FPM Settings
Edit the PHP-FPM configuration to improve WordPress performance and allow larger file uploads:
sudo nano /etc/php/8.5/fpm/php.ini
Adjust these values:
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 300
max_input_vars = 3000
memory_limit = 256M
cgi.fix_pathinfo = 0
The cgi.fix_pathinfo=0 setting is a security hardening measure — without it, PHP-FPM may attempt to execute the nearest PHP file when a requested path doesn’t exist, which can be exploited to execute arbitrary files.
Restart PHP-FPM after changes:
sudo systemctl restart php8.5-fpm
Step 4: Install MariaDB
WordPress works with MySQL or MariaDB. MariaDB is the recommended choice on Ubuntu — it is the default database in most Ubuntu deployments and a fully compatible drop-in replacement for MySQL.
Check the current stable release: mariadb.org/download
Check LTS vs. rolling release versions:endoflife.date/mariadb
As of early 2026, MariaDB 11.8 is the current long-term stable (LTS) release. Ubuntu’s default repositories may include an older version — use the official MariaDB repository for the latest LTS:
curl -LsS https://r.mariadb.com/downloads/mariadb_repo_setup \
| sudo bash -s -- --mariadb-server-version="mariadb-11.8"
sudo apt update
sudo apt install mariadb-server -y
Enable and start MariaDB:
sudo systemctl enable mariadb
sudo systemctl start mariadb
Run the security hardening script:
sudo mysql_secure_installation
When prompted: set a strong root password, remove anonymous users, disallow remote root login, remove the test database, and reload privileges.
Verify MariaDB version:
mariadb --version
Create the WordPress Database
Log into MariaDB as root:
sudo mariadb -u root -p
Run the following, replacing the placeholders with your own values:
CREATE DATABASE wordpress_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'your_strong_password';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wp_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Step 5: Download and Configure WordPress
Check the latest WordPress release: wordpress.org/download
Download the latest version directly:
cd /tmp
curl -O https://wordpress.org/latest.tar.gz
tar xzvf latest.tar.gz
sudo mv wordpress /var/www/your-domain.com
Set the correct ownership and permissions:
sudo chown -R www-data:www-data /var/www/your-domain.com
sudo find /var/www/your-domain.com -type d -exec chmod 755 {} \;
sudo find /var/www/your-domain.com -type f -exec chmod 644 {} \;
Configure wp-config.php
Copy the sample config and edit it:
cd /var/www/your-domain.com
sudo cp wp-config-sample.php wp-config.php
sudo nano wp-config.php
Update the database credentials:
define( 'DB_NAME', 'wordpress_db' );
define( 'DB_USER', 'wp_user' );
define( 'DB_PASSWORD', 'your_strong_password' );
define( 'DB_HOST', 'localhost' );
define( 'DB_CHARSET', 'utf8mb4' );
Replace the placeholder salts by visiting api.wordpress.org/secret-key/1.1/salt and pasting the result into the corresponding section of wp-config.php.
Step 6: Configure Nginx for WordPress
Create a new Nginx server block for your site:
sudo nano /etc/nginx/sites-available/your-domain.com
Paste the following configuration, replacing your-domain.com and the PHP version as appropriate:
server {
listen 80;
listen [::]:80;
server_name your-domain.com www.your-domain.com;
root /var/www/your-domain.com;
index index.php index.html index.htm;
# Logging
access_log /var/log/nginx/your-domain.com.access.log;
error_log /var/log/nginx/your-domain.com.error.log;
# Max upload size — match your php.ini setting
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.5-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
include fastcgi_params;
}
# Block access to sensitive files
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;
}
# Static file caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires max;
log_not_found off;
}
}
Enable the site and test the configuration:
sudo ln -s /etc/nginx/sites-available/your-domain.com \
/etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 7: SSL with Let’s Encrypt
Install Certbot and the Nginx plugin:
sudo apt install certbot python3-certbot-nginx -y
Obtain and install a certificate:
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
Certbot will automatically modify your Nginx config to redirect HTTP to HTTPS and add the SSL directives. Renewal is automatic via a systemd timer — verify it with:
sudo systemctl status certbot.timer
Test renewal manually:
sudo certbot renew --dry-run
Step 8: Complete the WordPress Installation
Visit https://your-domain.com in a browser. You should see the WordPress installation wizard. Complete the setup with your site title, admin username, password, and email.
Things to Check After Installation
Site health: In the WordPress admin, go to Tools → Site Health. This reports PHP version, database version, HTTPS status, file permissions, and recommended configuration values. Address any critical issues it flags.
Verify PHP version in use:
php --version
# or check via WP-CLI:
wp --info
Check Nginx error logs if anything seems wrong:
sudo tail -f /var/log/nginx/your-domain.com.error.log
sudo tail -f /var/log/nginx/error.log
Check PHP-FPM logs:
sudo tail -f /var/log/php8.5-fpm.log
Check MariaDB is running:
sudo systemctl status mariadb
sudo mariadb -u wp_user -p -e "SELECT VERSION();"
Confirm WordPress can write to uploads:
ls -la /var/www/your-domain.com/wp-content/uploads
# should be owned by www-data
Apache (Brief Reference)
If you prefer Apache, the installation follows the same database and PHP steps above. Replace the Nginx steps with the following.
Install Apache:
sudo apt install apache2 -y
sudo systemctl enable apache2
sudo a2enmod rewrite
Install PHP for Apache instead of PHP-FPM:
sudo apt install php8.5 libapache2-mod-php8.5 php8.5-mysql \
php8.5-curl php8.5-gd php8.5-mbstring php8.5-xml php8.5-zip \
php8.5-imagick php8.5-intl php8.5-bcmath -y
Create an Apache virtual host:
sudo nano /etc/apache2/sites-available/your-domain.com.conf
<VirtualHost *:80>
ServerName your-domain.com
ServerAlias www.your-domain.com
DocumentRoot /var/www/your-domain.com
<Directory /var/www/your-domain.com>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/your-domain.com.error.log
CustomLog ${APACHE_LOG_DIR}/your-domain.com.access.log combined
</VirtualHost>
Enable the site:
sudo a2ensite your-domain.com.conf
sudo apache2ctl configtest
sudo systemctl reload apache2
WordPress’s .htaccess file handles permalinks automatically with AllowOverride All in place. Use sudo certbot --apache for SSL instead of --nginx.
Nginx is generally preferred for WordPress due to lower memory usage and better handling of concurrent connections. Apache remains a solid choice, particularly if you need .htaccess-based plugin compatibility or are more familiar with its configuration model.
Migrating an Existing WordPress Instance
If you are moving an existing WordPress site to this server rather than starting fresh, there are two approaches: migrate before completing the WordPress setup (replacing the fresh install entirely) or migrate into an existing running WordPress instance using an import plugin.
Option 1: Migrate First, Then Configure (Duplicator)
Duplicator is the most widely used WordPress migration plugin and handles full-site migration including files, database, and configuration in a self-contained installer package.
On the source site:
- Install and activate the Duplicator plugin
- Go to Duplicator → Packages → Create New
- Run through the wizard — it scans your site and builds a
.ziparchive and aninstaller.phpfile - Download both files to your local machine
On the destination server:
- Create the database and user as described in Step 4 above
- Upload both the
.zipandinstaller.phpto your web root (/var/www/your-domain.com/) — you can usescpor SFTP - Visit
https://your-domain.com/installer.phpin a browser - Follow the installer wizard: it extracts the archive, connects to the new database, and updates all URLs and paths automatically
- Delete
installer.phpand the.zipfile after migration completes
Verify the migration:
# Check the site URL is correctly updated in the database
sudo mariadb -u wp_user -p wordpress_db \
-e "SELECT option_name, option_value FROM wp_options WHERE option_name IN ('siteurl','home');"
Duplicator Pro supports larger sites and staged migration. The free version handles most sites under a few gigabytes without issue.
Option 2: Migrate Into an Existing WordPress Install
If the destination WordPress instance is already running, you can import content without replacing the entire installation.
For content only (posts, pages, media):
The built-in WordPress importer handles WXR (WordPress eXtended RSS) export files. On the source site, go to Tools → Export, export all content, then on the destination site go to Tools → Import → WordPress and upload the file. Media attachments can be downloaded automatically during import if the source site is publicly accessible.
For a full-site migration into an existing install:
All-in-One WP Migration is a popular alternative to Duplicator that works as both an exporter and importer within the WordPress admin. Export from the source, import on the destination — it handles URL replacement automatically. The free version has a file size limit on import; the paid extension removes it.
Manual migration (most control):
- On the source server, export the database:
mysqldump -u wp_user -p wordpress_db > wordpress_backup.sql
- Copy the
wp-contentdirectory (themes, plugins, uploads) to the new server:
rsync -avz /var/www/old-site/wp-content/ \
user@new-server:/var/www/your-domain.com/wp-content/
- Import the database on the new server:
sudo mariadb -u wp_user -p wordpress_db < wordpress_backup.sql
- Update the site URL in the database to reflect the new domain:
sudo mariadb -u wp_user -p wordpress_db -e "
UPDATE wp_options SET option_value='https://your-domain.com'
WHERE option_name='siteurl' OR option_name='home';"
- If the domain has changed, run a search-and-replace on serialized database content using WP-CLI:
wp search-replace 'https://old-domain.com' 'https://your-domain.com' \
--all-tables --precise
WP-CLI is the correct tool for this — a naive SQL find-and-replace on serialized data will corrupt the database. Install WP-CLI with:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
After Any Migration: Verification Checklist
- Visit the site and check pages, images, and menus load correctly
- Log into the WordPress admin and verify Tools → Site Health shows no critical errors
- Test that media uploads work (try uploading an image)
- Confirm permalinks are correct: go to Settings → Permalinks and click Save (this flushes rewrite rules)
- Check file permissions are set to
www-dataownership - Confirm SSL is active and the site URL in Settings → General uses
https:// - If using a caching plugin, flush all caches after migration
This guide is part of a broader set of resources on working with WordPress in modern web application stacks. If you’re using this WordPress installation as a headless backend for a Next.js, Nuxt, SvelteKit, TanStack, or similar frontend architecture, wp-block-styles is a zero-dependency npm package I built to solve the block styling gap in headless WordPress — a single import styles all Gutenberg blocks rendered via content.rendered.