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 sudo privileges
  • 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:

  1. Install and activate the Duplicator plugin
  2. Go to Duplicator → Packages → Create New
  3. Run through the wizard — it scans your site and builds a .zip archive and an installer.php file
  4. Download both files to your local machine

On the destination server:

  1. Create the database and user as described in Step 4 above
  2. Upload both the .zip and installer.php to your web root (/var/www/your-domain.com/) — you can use scp or SFTP
  3. Visit https://your-domain.com/installer.php in a browser
  4. Follow the installer wizard: it extracts the archive, connects to the new database, and updates all URLs and paths automatically
  5. Delete installer.php and the .zip file 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):

  1. On the source server, export the database:
mysqldump -u wp_user -p wordpress_db > wordpress_backup.sql
  1. Copy the wp-content directory (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/
  1. Import the database on the new server:
sudo mariadb -u wp_user -p wordpress_db < wordpress_backup.sql
  1. 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';"
  1. 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-data ownership
  • 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.