How to Host a WordPress Site on a Free VPS (Apache & MySQL) 2026

Host a WordPress Site on a Free VPS

Shared hosting works until it doesn’t — slow page loads, resource limits, no control over PHP versions, and the moment your site gets a traffic spike it either dies or you’re hit with an upgrade bill.

A VPS gives you a dedicated environment for WordPress that you control entirely. You choose the PHP version, configure Apache exactly as you want, tune MySQL for your workload, and no other tenant can affect your performance. And with a free VPS, you pay nothing to start.

This guide is specifically Apache + MySQL — if you want Nginx instead, we have a separate guide on installing WordPress with Nginx on a free VPS. Apache is the better choice for WordPress when you need .htaccess support, which most WordPress plugins and permalink structures depend on.


Why Host WordPress on a VPS Instead of Shared Hosting

Factor Shared Hosting Free VPS (VPSWala)
PHP version control Limited — provider decides ✅ Install any version you want
MySQL access phpMyAdmin only, restricted ✅ Full root MySQL access
Performance consistency ❌ Shared resources — noisy neighbours ✅ Dedicated CPU and RAM
OPcache control ❌ Usually disabled or limited ✅ Full OPcache configuration
Apache/Nginx config ❌ No access ✅ Full server config control
Multiple sites Limited by plan ✅ Unlimited via Apache virtual hosts
Cost $3–15/month ✅ Free forever (Starter) or 30-day trial

The performance difference between shared hosting and a VPS is immediate and measurable. On shared hosting, every PHP request waits in line behind other tenants’ traffic. On a VPS, Apache serves your WordPress site with dedicated resources and no queue.


What You Need Before You Start

  • A VPS running Ubuntu 22.04 or 24.04 — minimum 1 vCPU, 1 GB RAM
  • Root or sudo SSH access
  • A domain name — pointed to your VPS IP address (required for SSL in Step 9)
  • About 20 minutes

DNS setup first: Before starting, add an A record for your domain pointing to your VPS IP. DNS propagation takes up to 48 hours — if you start Step 9 before DNS has propagated, Let’s Encrypt will fail. You can test everything else using your VPS IP while waiting for DNS.


Step 1 — Get a Free VPS and Connect

If you don’t have a VPS yet, VPSWala’s free VPS Server deploys Ubuntu 22.04 or 24.04 in 60 seconds — no credit card required. The Starter plan (ARM64, 2 GB RAM) handles a personal WordPress site or low-traffic blog comfortably. The 30-day Professional free trial (8-core AMD EPYC, 8 GB DDR5 ECC RAM) handles 10–20 WordPress sites without breaking a sweat.

Connect via SSH once deployed:

ssh root@YOUR_VPS_IP

If you’re connecting for the first time and want to set up SSH key authentication for security, our SSH tips guide covers it in detail.


Step 2 — Update and Prepare the Server

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget unzip

Takes 1–2 minutes. Never skip on a fresh VPS — you may otherwise hit dependency conflicts when installing Apache or PHP later.

After your first login on a new VPS, check our list of 5 things you must do after launching a Linux VPS — covers creating a sudo user, disabling root SSH login, and other baseline hardening that applies before any service installs.


Step 3 — Install Apache Web Server

sudo apt install -y apache2
sudo systemctl start apache2
sudo systemctl enable apache2

Check Apache is running:

sudo systemctl status apache2

Should show active (running). Now open the firewall:

sudo ufw allow OpenSSH
sudo ufw allow 'Apache Full'
sudo ufw enable
sudo ufw status

Visit http://YOUR_VPS_IP in a browser — you should see the Ubuntu Apache default page. That confirms Apache is installed and publicly reachable.

Cloud firewall note: If you can’t reach Apache in the browser despite the above showing correctly, check your VPS provider’s external firewall. Most providers (including VPSWala via Proxmox) have a separate network-level firewall — ports 80 and 443 need to be open there too, not just in UFW. This is the most common reason Apache appears running but can’t be reached from the internet. See our guide on opening ports 80 and 443 for the full walkthrough.


Step 4 — Install MySQL and Create WordPress Database

sudo apt install -y mysql-server
sudo systemctl start mysql
sudo systemctl enable mysql

Run the security wizard:

sudo mysql_secure_installation
  • Validate password plugin: Y
  • Password strength: 1 (MEDIUM) or 2 (STRONG)
  • Remove anonymous users: Y
  • Disallow remote root login: Y
  • Remove test database: Y
  • Reload privilege tables: Y

Create the WordPress database and a dedicated user — never connect WordPress directly with the MySQL root account:

sudo mysql -u root
CREATE DATABASE wordpress_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_520_ci;
CREATE USER 'wordpress_user'@'localhost' IDENTIFIED BY 'YourStrongPassword123!';
GRANT ALL PRIVILEGES ON wordpress_db.* TO 'wordpress_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Write down the database name, username, and password — you’ll need them in Step 6.

💡 Tip: Use utf8mb4 charset when creating your database — it supports the full Unicode range including emojis. The older utf8 charset in MySQL is actually UTF-8 limited to 3 bytes and will cause silent data corruption when users post emoji content.

Step 5 — Install PHP 8.3 and WordPress Extensions

Ubuntu 24.04 ships PHP 8.3 natively. For Ubuntu 22.04, add the Ondřej PPA first:

# Ubuntu 22.04 ONLY
sudo add-apt-repository ppa:ondrej/php -y
sudo apt update

Install PHP and every extension WordPress needs or recommends:

sudo apt install -y php8.3 libapache2-mod-php8.3 php8.3-mysql \
php8.3-curl php8.3-gd php8.3-mbstring php8.3-xml php8.3-zip \
php8.3-intl php8.3-opcache php8.3-bcmath php8.3-imagick \
php8.3-soap php8.3-cli

Enable PHP in Apache and restart:

sudo a2enmod php8.3
sudo systemctl restart apache2

Verify PHP version:

php -v

Check all WordPress-required extensions are installed:

php -m | grep -E 'curl|gd|mbstring|xml|zip|intl|opcache|imagick|mysql'

Every extension you installed should appear in that list. If any are missing, install them individually: sudo apt install php8.3-MISSING_EXTENSION.


Step 6 — Download and Configure WordPress

Create the directory for your site:

sudo mkdir -p /var/www/yourdomain.com
cd /var/www/yourdomain.com

Download the latest WordPress directly from wordpress.org:

sudo wget https://wordpress.org/latest.tar.gz
sudo tar -xzf latest.tar.gz
sudo mv wordpress/* .
sudo rm -rf wordpress latest.tar.gz

Create and configure wp-config.php:

sudo cp wp-config-sample.php wp-config.php
sudo nano wp-config.php

Update the database credentials block:

define( 'DB_NAME',     'wordpress_db' );
define( 'DB_USER',     'wordpress_user' );
define( 'DB_PASSWORD', 'YourStrongPassword123!' );
define( 'DB_HOST',     'localhost' );
define( 'DB_CHARSET',  'utf8mb4' );
define( 'DB_COLLATE',  '' );

Replace the security keys section by visiting https://api.wordpress.org/secret-key/1.1/salt/, copying the generated output, and replacing the matching block in wp-config.php. This is a security requirement — don’t skip it.

While you have wp-config.php open, add these two lines anywhere before the line that says /* That's all, stop editing! */:

define( 'WP_DEBUG', false );
define( 'FS_METHOD', 'direct' );

FS_METHOD = direct tells WordPress to write files directly without FTP credentials — required when Apache runs as www-data and you’ve set file ownership correctly in the next step.


Step 7 — Configure Apache Virtual Host for WordPress

Enable the Apache rewrite module — WordPress permalinks and most plugins depend on it:

sudo a2enmod rewrite

Create the virtual host configuration file:

sudo nano /etc/apache2/sites-available/yourdomain.com.conf
<VirtualHost *:80>
    ServerName yourdomain.com
    ServerAlias www.yourdomain.com
    DocumentRoot /var/www/yourdomain.com
 
    ErrorLog ${APACHE_LOG_DIR}/yourdomain.com-error.log
    CustomLog ${APACHE_LOG_DIR}/yourdomain.com-access.log combined
 
    <Directory /var/www/yourdomain.com>
        AllowOverride All
        Require all granted
        Options -Indexes +FollowSymLinks
    </Directory>
 
    # WordPress security: block access to sensitive files
    <FilesMatch "^(wp-config\.php|\.htaccess|readme\.html|license\.txt)$">
        Require all denied
    </FilesMatch>
 
    # Block direct access to PHP files in uploads
    <Directory /var/www/yourdomain.com/wp-content/uploads>
        php_admin_flag engine Off
    </Directory>
</VirtualHost>

The security blocks at the bottom are WordPress-specific hardening — they prevent direct access to wp-config.php from the web and block PHP execution in the uploads directory (a common attack vector for uploaded malicious files).

Enable the site, disable the default site, test the config, and reload:

sudo a2ensite yourdomain.com.conf
sudo a2dissite 000-default.conf
sudo apache2ctl configtest
sudo systemctl reload apache2

apache2ctl configtest must return Syntax OK before you reload. If it returns errors, fix them before proceeding.


Step 8 — Set Correct File Permissions

WordPress file permissions are the single most common source of both security vulnerabilities and “unable to update” errors. The correct setup:

# Set ownership to www-data (Apache's user)
sudo chown -R www-data:www-data /var/www/yourdomain.com
 
# Directories: 755 — owner can write, others can read/execute
sudo find /var/www/yourdomain.com -type d -exec chmod 755 {} \;
 
# Files: 644 — owner can write, others can read
sudo find /var/www/yourdomain.com -type f -exec chmod 644 {} \;
 
# wp-config.php: 640 — only owner and group can read, no world access
sudo chmod 640 /var/www/yourdomain.com/wp-config.php

What this means in practice:

  • Apache can write to files — plugin installs, theme updates, media uploads all work without FTP
  • Nobody else on the server can read your wp-config.php credentials
  • No world-writable files — prevents attackers from modifying your site files even if they find a PHP injection vulnerability
💡 Common mistake: Setting everything to 777 because an error told you to. 777 means the world can write to your files. Any attacker who can execute code on your server can then overwrite your WordPress files. Never use 777 on a live site.

Step 9 — Add Free SSL with Let’s Encrypt

Google has marked HTTP sites as “Not Secure” since 2018. SSL is not optional for any site you care about ranking. Let’s Encrypt makes it free and automatic:

sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com

Certbot prompts:

  • Email address — for expiry notifications
  • Agree to terms — A
  • Share email with EFF — your choice
  • Redirect HTTP to HTTPS — choose 2 — forces all traffic to HTTPS

After Certbot finishes, it automatically updates your Apache virtual host with the SSL configuration and sets up a cron job for auto-renewal. Test renewal:

sudo certbot renew --dry-run

Verify your site is now serving HTTPS by visiting https://yourdomain.com. You should see a padlock in the browser address bar. If you still see HTTP, check that Apache is reloaded: sudo systemctl reload apache2.


Step 10 — Complete the WordPress Installation

Visit https://yourdomain.com in your browser. WordPress will detect that it hasn’t been installed and launch the installation wizard:

  1. Select your language
  2. Enter site title
  3. Create admin username and a strong password
  4. Enter your email address
  5. Decide whether search engines should index the site — uncheck if you’re still building it
  6. Click Install WordPress

Log into the dashboard at https://yourdomain.com/wp-admin.

First Things to Do After Installing

  • Settings → Permalinks: Set to “Post name” (/%postname%/). This enables clean URLs and is required for most plugins and themes. Click Save Changes to write the .htaccess rewrite rules
  • Settings → General: Confirm WordPress Address and Site Address both use https://
  • Appearance → Themes: Install a theme
  • Plugins → Add New: Install your essential plugins — see our list of 16 essential WordPress plugins
  • Dashboard → Updates: Run all available updates

Step 11 — Performance Tuning

1. Configure OPcache

OPcache caches compiled PHP bytecode in memory so PHP doesn’t re-parse your WordPress files on every request. It’s installed — it just needs tuning:

sudo nano /etc/php/8.3/apache2/php.ini

Find and update these values (use Ctrl+W to search):

; OPcache
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.fast_shutdown=1
 
; Upload limits for media
upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
max_execution_time = 300
max_input_vars = 3000
sudo systemctl restart apache2

2. Enable Apache Compression

sudo a2enmod deflate expires headers
sudo systemctl reload apache2

Add browser caching headers to your WordPress .htaccess file (above the WordPress rules):

sudo nano /var/www/yourdomain.com/.htaccess
# Browser caching
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType text/css "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"
    ExpiresByType image/jpeg "access plus 1 month"
    ExpiresByType image/png "access plus 1 month"
    ExpiresByType image/webp "access plus 1 month"
    ExpiresByType image/gif "access plus 1 month"
    ExpiresByType image/x-icon "access plus 1 year"
    ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
 
# Gzip compression
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/css
    AddOutputFilterByType DEFLATE application/javascript application/json
    AddOutputFilterByType DEFLATE image/svg+xml
</IfModule>
 
# BEGIN WordPress
# (WordPress adds its rules below here automatically)

3. Install a WordPress Caching Plugin

Caching plugins serve pre-built HTML files directly — skipping PHP and MySQL entirely on cached page loads. This is the single highest-impact performance improvement you can make:

  • W3 Total Cache — most configurable, works well with Apache
  • WP Super Cache — simpler, generates static HTML files that Apache serves directly
  • LiteSpeed Cache — best performance but requires LiteSpeed server (not Apache)

For Apache on VPS, install WP Super Cache from Plugins → Add New. Enable it and set cache type to “Simple”. Your page load times should drop significantly on repeat visits.

Performance Summary

Optimization Impact on WordPress What to do
OPcache Very high — 30–50% PHP speedup Configured in Step 11 above
Page caching plugin Very high — skips PHP/MySQL entirely Install WP Super Cache or W3 Total Cache
Gzip compression High — 60–80% smaller HTML/CSS/JS mod_deflate enabled above
Browser caching Medium — faster repeat visits mod_expires configured in .htaccess above
Image optimization High — images are usually 60–80% of page weight Install Smush or ShortPixel plugin
CDN (Cloudflare free) High for global visitors Proxy your domain through Cloudflare free plan

Step 12 — Security Hardening

1. Change Default WordPress Login URL

Every WordPress site on the internet gets hammered by bots attempting to log in at /wp-login.php. Install the WPS Hide Login plugin and change your login URL to something only you know. Drops brute force attempts to near zero. For more on preventing brute force attacks, read our guide on how to prevent brute force attacks in a few steps.

2. Disable XML-RPC if Not Needed

XML-RPC is used for remote publishing tools and the Jetpack plugin. If you don’t use either, disable it — it’s a common DDoS amplification vector:

Add to your .htaccess:

# Disable XML-RPC
<Files xmlrpc.php>
    Require all denied
</Files>

3. Block WordPress Enumeration

By default WordPress exposes usernames at /?author=1. Block it by adding to .htaccess:

# Block author enumeration
RewriteCond %{QUERY_STRING} ^author=\d
RewriteRule ^ /? [L,R=301]

4. Set Up Automatic Backups

Install the UpdraftPlus plugin — free tier supports scheduled backups to Google Drive, Dropbox, or your VPS itself. Configure daily backups of both files and database. Also set up server-level automated backups via our guide on backing up and restoring VPS data.

5. Install a Security Plugin

Install Wordfence Security — free tier includes malware scanner, firewall, and login security. Run a full scan after installation and fix any flagged issues. Also see our guide on securing and firewalling your VPS server for server-level hardening beyond WordPress.


Troubleshooting

Problem Likely Cause Fix
WordPress shows raw PHP code PHP module not loaded in Apache sudo a2enmod php8.3 && sudo systemctl restart apache2
404 on all pages except homepage mod_rewrite disabled or AllowOverride not set Run sudo a2enmod rewrite, ensure AllowOverride All is in virtual host config, then go to Settings → Permalinks → Save Changes
Error establishing database connection Wrong credentials in wp-config.php Double-check DB_NAME, DB_USER, DB_PASSWORD, DB_HOST in wp-config.php match exactly what you created in Step 4
Can’t upload images — too large PHP upload limits too low Set upload_max_filesize = 64M and post_max_size = 64M in php.ini, restart Apache
Can’t install or update plugins Wrong file ownership or FS_METHOD not set Run sudo chown -R www-data:www-data /var/www/yourdomain.com and add define('FS_METHOD','direct') to wp-config.php
White screen of death PHP fatal error or memory limit Check sudo tail -50 /var/log/apache2/error.log. Temporarily set WP_DEBUG to true in wp-config.php to see the exact error
SSL certificate fails DNS not propagated or port 443 blocked Run dig yourdomain.com +short — must return your VPS IP. Check port 443 is open in both UFW and your cloud firewall
Site loads over HTTP despite SSL WordPress URL settings still HTTP Go to Settings → General → change WordPress Address and Site Address to https://
Mixed content warnings (padlock broken) Some resources still loading over HTTP Install Really Simple SSL plugin — it fixes mixed content automatically

Your WordPress site is running on a server you fully control. Apache handles incoming requests, MySQL stores your content, PHP 8.3 with OPcache processes it fast, and Let’s Encrypt keeps your SSL certificate current automatically.

The VPSWala Professional free trial — 8-core AMD EPYC, 8 GB DDR5 ECC RAM, 1 TB Micron NVMe — comfortably handles 10–20 WordPress sites simultaneously. That’s performance that rivals $30–50/month plans at other providers, free for 30 days with no credit card required.

If you prefer Nginx over Apache for your WordPress stack, see our guide on installing WordPress with Nginx on a free VPS. For setting up multiple WordPress sites on the same VPS, check our posts on subdomains with Let’s Encrypt and running multiple VPS instances. Need a Windows RDP server instead? grab a free RDP server here.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *