How to install WordPress on a VPS
Updated
To run WordPress on a VPS, install a web server (here Caddy), PHP-FPM with the extensions WordPress needs, and MariaDB; create a database and user; download WordPress with WP-CLI; and point a domain at the server so Caddy can enable HTTPS. On Ubuntu 24.04 the whole setup takes about 20 minutes.
If you would rather skip the manual work, the WordPress one-click app on Wolkra installs the same stack automatically. This guide shows what happens underneath, so you can run and fix it yourself.
Before you start
- A server with Ubuntu 24.04 (Debian 12 works with the same steps), 2 GB of memory recommended.
- The first steps done: updates, sudo user, SSH keys.
- A domain with an A record (and AAAA if you use IPv6) pointing to the server. In this guide:
blog.example.com. - Ports 80 and 443 open in UFW and in any cloud firewall.
sudo ufw allow 80,443/tcp
1. Install PHP and MariaDB
sudo apt update
sudo apt install -y mariadb-server \
php-fpm php-mysql php-curl php-gd php-intl php-mbstring \
php-xml php-zip php-imagick php-bcmath unzip
Ubuntu 24.04 installs PHP 8.3; Debian 12 installs PHP 8.2. Check which one you have:
php -v
ls /run/php/
Note the socket name, such as php8.3-fpm.sock. You need it in step 5.
2. Secure MariaDB and create the database
sudo mariadb-secure-installation
Accept the defaults: keep unix socket authentication for root, remove anonymous users and the test database.
Create a database and user for WordPress:
DBPASS=$(openssl rand -hex 20)
echo "Database password: $DBPASS"
sudo mariadb <<SQL
CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'wp'@'localhost' IDENTIFIED BY '$DBPASS';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wp'@'localhost';
FLUSH PRIVILEGES;
SQL
Save the password; WP-CLI needs it shortly.
3. Install Caddy
Caddy publishes an official Debian/Ubuntu repository:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy
4. Install WP-CLI and download WordPress
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
php wp-cli.phar --info
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
Create the site directory and let the PHP user own it:
sudo mkdir -p /var/www/blog
sudo chown www-data:www-data /var/www/blog
cd /var/www/blog
sudo -u www-data wp core download
sudo -u www-data wp config create \
--dbname=wordpress --dbuser=wp --dbpass="$DBPASS" --dbhost=localhost
sudo -u www-data wp core install \
--url=https://blog.example.com \
--title="My Blog" \
--admin_user=admin_$(openssl rand -hex 3) \
[email protected] \
--prompt=admin_password
A non-obvious admin username makes guessing attacks harder. --prompt asks for the password instead of leaving it in your shell history.
5. Configure Caddy
Replace /etc/caddy/Caddyfile with:
blog.example.com {
root * /var/www/blog
encode zstd gzip
php_fastcgi unix//run/php/php8.3-fpm.sock
file_server
@blocked path /xmlrpc.php /wp-config.php /.git/* /.env
respond @blocked 404
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
Use your own PHP socket name. Then:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
Caddy requests a certificate on the first request. Open https://blog.example.com/wp-admin/ and log in. More options are in the Caddy reverse proxy guide.
6. Tune PHP
The defaults are small for media uploads. Create /etc/php/8.3/fpm/conf.d/99-wordpress.ini (adjust the version):
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
max_execution_time = 120
opcache.enable = 1
opcache.memory_consumption = 128
sudo systemctl restart php8.3-fpm
OPcache keeps compiled PHP in memory and is the biggest free speed gain.
7. Add caching
For most blogs a page-cache plugin is enough. For busier sites, add Redis as an object cache:
sudo apt install -y redis-server php-redis
sudo systemctl restart php8.3-fpm
cd /var/www/blog
sudo -u www-data wp plugin install redis-cache --activate
sudo -u www-data wp redis enable
Redis listens only on localhost by default. Keep it that way.
8. Run WordPress cron from the system
WordPress triggers scheduled tasks on page views, which is unreliable on quiet sites and wasteful on busy ones. Use a real cron job instead:
sudo -u www-data wp config set DISABLE_WP_CRON true --raw
echo '*/5 * * * * www-data cd /var/www/blog && /usr/local/bin/wp cron event run --due-now --quiet' | \
sudo tee /etc/cron.d/wordpress
9. Keep it updated
Most WordPress compromises come through outdated plugins. Update weekly, or automate:
cd /var/www/blog
sudo -u www-data wp core update
sudo -u www-data wp plugin update --all
sudo -u www-data wp theme update --all
Remove plugins and themes you don't use. Each one is code that can have a hole.
10. Back up
You need two things: the database and wp-content.
sudo mkdir -p /var/backups/blog
cd /var/www/blog
sudo -u www-data wp db export - | gzip | sudo tee /var/backups/blog/db-$(date +%F).sql.gz >/dev/null
sudo tar czf /var/backups/blog/files-$(date +%F).tar.gz -C /var/www/blog wp-content
Schedule that nightly and copy it somewhere else. On Wolkra, add automatic daily server backups (20% of the server price, 7 restore points) and take a snapshot before major updates. See the backup strategy guide.
11. Harden the basics
- Keep SSH on keys only and the OS patched (secure an Ubuntu server).
- Turn on two-factor login for WordPress admins with a plugin.
- Disable file editing in the dashboard:
sudo -u www-data wp config set DISALLOW_FILE_EDIT true --raw
- Limit login attempts with a plugin or Fail2ban.
12. Send email reliably
WordPress sends password resets, comment notices and order mails through PHP's mail() by default, which often lands in spam. Use an SMTP plugin and a proper mail provider, and add SPF and DKIM records for your domain as the provider describes. If the server sends mail itself, set reverse DNS for its IP to the server's hostname in the console; see IP addresses.
13. Check that everything works
A quick health check after setup:
curl -I https://blog.example.com
sudo -u www-data wp --path=/var/www/blog core verify-checksums
sudo -u www-data wp --path=/var/www/blog cron event list
systemctl status caddy php8.3-fpm mariadb --no-pager
verify-checksums compares core files with the official release and is also a quick way to spot tampering later.
Sizing and growth
Watch memory and CPU in the console graphs for a few weeks. If PHP workers run out, increase pm.max_children in /etc/php/8.3/fpm/pool.d/www.conf or resize the server to a bigger plan. Traffic is unmetered on every Wolkra plan, so a busy month does not raise the bill. For more on running sites this way see WordPress hosting.
Frequently asked questions
Is a VPS better than shared hosting for WordPress?
A VPS gives you your own resources, full control over PHP and caching, and no noisy neighbours on the same account. You also take on updates and security, which shared hosting does for you.
How much memory does WordPress need on a VPS?
A single site with a database runs on 1 GB with care. 2 GB is more comfortable, and busy stores with WooCommerce often want 4 GB.
Why use Caddy instead of Nginx or Apache?
Caddy obtains and renews HTTPS certificates on its own and needs only a few lines of config. Nginx and Apache work just as well but need more setup.
Can I get WordPress pre-installed?
Yes. The Wolkra WordPress one-click app sets up Caddy, PHP, MariaDB and WP-CLI for you, ready a few minutes after the server boots.
How do I back up WordPress?
Back up both the database and the wp-content folder. A nightly WP-CLI database export plus a file copy, stored off the server, covers it.