WordPress Self Hosted A Guided Tour To Running Wordpress Independent Of Big Hosting
Self hosting wordpress can save you money, but more importantly, give you peace of mind owning your own data. You will learn a lot about Linux on the way.
What Is The Meaning Of Self-Hosted WordPress?
Large WordPress hosting companies need to justify themselves to their corporate investing firms. Many of these companies receive large injections of cash into their business. In exchange, they must prove that they can generate a profit, else they risk de-funding from their corporate overlords.
When viewed in this context, it is easy to see how VC Funding / corporate investment groups are the actual customers for these platforms, and the sites they host are a means to extract value for continued financial support.
Self-Hosted WordPress is a way to reclaim some of this value for yourself. In its simplest form, Self-Hosted WordPress is the process of managing the server that WordPress is hosted on yourself. This involves using something like a virtual private server. This is a virtual computer that is provided to an end user which is allocated server resources. This is on a physical server, but ofte times resources are shared among users.
The advantage of this is that it offers users a greater level of control over their server, but helps keep costs under control since resources are being divided. A downside to this is that some users can use more resources than others, causing a disparity in performance.
Self hosted WordPress can give you finer control over your website data, and has a ton of advantages that large hosting firms like WP Engine, Pluggable, Bluehost, Hostinger, etc., only offer at additional cost.
What Are WordPress VPS Requirements?
If you head on over to WordPress Requirements page, you will find out some very basic information:
- PHP, the programming language that runs WordPress on the server, must be version 7.4 or greater.
- MYSQL version 8.0 or greater OR MariaDB version 10.5 or greater
- HTTPS Support
They also recommend Apache or Nginx, and they also have a link to a “detailed” PHP Extension recommendation list.
Unfortunately, WordPress falls short on recommending hardware requirements. For this, I will just provide my own estimates based on experience:
I have successfully installed WordPress on a 1 core, 512MB ram virtual private server, but it was memory constrained even at low traffic. Mariadb/MYSQL takes about around 200MB of ram, so that doesn’t leave a lot of overhead left after you take away system memory usage, additional services.
I would say at the very least you need a 1 core / 1GB Ram VPS, but I recommend at least 2 core / 2 GB ram.
This, in my experience, has provided more than enough overhead for small sites to grow and can allow additional flexibility for 1 or 2 more services installed on your vps later.
Get A Virtual Private Server For WordPress
Head over to Hetzner Cloud and get yourself signed up for an account. There is a short verification process that entails giving them a photo of your personal information. If you do not feel comfortable with that, Contabo, Linode, or Vultr are also additional VPS hosting options, which are acceptable. I chose Hetzner Cloud because they don’t seem to stuff as many customers on one server giving a bit more performance in my opinion.
Once you have selected a vps provider, it is time to start configuring our server to run WordPress.
VPS Security Guide
I have a full article on configuring a virtual private server and hardening.
If you need additional information on this subject, I would recommend checking there first.
This section assumes that you have some familiarity with SSH. If you need to learn about that, or get a refresher, here is a link to a Digital Ocean Article: SSH Essentials: Working with SSH Servers, Clients, and Keys.
To get started, you need to know your IP Address.
from your computer, open up your favorite terminal and run ssh root@your_ip_address
.
Install Uncomplicated Firewall (UFW) For Linux
sudo apt update && sudo apt upgrade -y
which ufw
. If nothing is returned, thensudo apt install ufw
ufw allow OpenSSH
ufw enable
ufw status
This will configure ufw firewall to allow ssh connections, but lock down others.
Add A User
We can add a user so that later we can lock down root, and help prevent brute force attacks on our server.
adduser your_username
usermod -aG sudo your_username
su - your_username
Making sure we can access our server via ssh only
On your local computer:
ssh-keygen -t rsa
ssh-copy-id your_username@your_server_ip
On your VPS:
cd /etc/ssh
sudo cp sshd_config sshd_config.dist
- this makes a backup of the config in case we need to roll backsudo vi sshd_config
- note: I actually did sudo apt install vim and used vim instead, the rest of this will be vim related.- PasswordAuthorization should be set to
no
- PermitRootLogin should be set to
no
- MaxAuthTries should be set to 3 or any other number you see fit
- PermitEmptyPasswords should be set to
no
- make sure they are uncommented
- Adjust anything else you may want to such as KerberosAuthentication or GSSAPI Authentication
- save the file and run
sudo systemctl restart sshd
Before you go any further, open up a new terminal on your local computer, and verify you can access the VPS via ssh.
Installing Fail2Ban To Prevent Brute Force Attacks, DDoS, and Dictionary
Attacks
sudo apt install fail2ban
sudo systemctl start fail2ban
sudo systemctl enable fail2ban
sudo vim /etc/fail2ban/jail.local
jail.local file config
[sshd]
enable = true
port = 22
filter = ssh
logpath = /var/log/auth.log
maxretry = 3
sudo systemctl restart fail2ban
Open Up UFW For Web Traffic So Visitors Can Access WordPress Site
sudo ufw allow www
sudo ufw allow https
Set Server Time
sudo dpkg-reconfigure tzdata
- enter user password
- follow prompts
Point Your DNS To Server For Wordpress
This will often be DNS Provider specific, but in general you now want to point your DNS to your server.
This typically involves setting up an A record with a value of @ that points to your ip address. Additionally, you may also want to configure a cname such as www, and configure an ipv6 if your vps provides you an adddress. Here is an example:
Record Type | Hostname | Value/Address | TTL | Description |
---|---|---|---|---|
A | example.com | your_server_ip4_address | 3600 | Points to an IPv4 address |
AAAA | example.com | 2001:0db8:85a3:0000:0000:8a2e:0370:7334 | 3600 | Points to an IPv6 address |
CNAME | www | example.com. | 3600 | Alias for another domain name |
Install PHP, NGINX, MariaDB
sudo apt install software-properties-common -y
sudo add-apt-repository ppa:ondrej/php
sudo apt update
sudo apt install php php-fpm php-mysql php-xml php-mbstring php-curl php-zip php-gd -y
sudo apt install nginx -y
sudo apt install mariadb-server -y
sudo systemctl start mariadb && sudo systemctl enable mariadb
sudo mysql
ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';
EXIT;
sudo mysql_secure_installtion
sudo systemctl status mariadb
mysql -u root -p
note: later on, we may need to grant access to the Wordpress table for this user
Verify Installation of PHP
- php -v
- nginx -v
Create A Wordpress Table
sudo mysql -u root -p
CREATE DATABASE wordpress CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'wpuser'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost';
exit;
Install WP-CLI For Managing 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
wp --info
- To Update:
wp cli update
Install WordPress With WP-CLI
wp core download --path=/var/www/yourwebsite.com --localte=en_US
- Generate A Config:
wp config create --dbname=your_db_name_here --dbuser_your_db_user_here --prompt=dbpass
. This will prompt you for the password. wp db create
wp core install --url=yourwebsite.com --title="Your Website Title" --admin_user=your_admin_user --admin_password=your_admin_password --admin_email=admin@email.com
Add Yourself to the www-data group, Configure WordPress File & Directory
Permissions
For running various tasks on the server, you may need to be added to the www-data group:
sudo chown -R your_username:www-data /var/www/yourwebsite.com
sudo find /var/www/yourwebsite.com -type d -exec chmod 755 {} +
sudo find /var/www/yourwebsite.com -type f -exec chmod 644 {} +
sudo chmod 600 /var/www/yourwebsite.com/wp-config.php
wp-content/uploads
should typically have 755 permissionsSince I am using nginx, I have also assigned www-data the owner
sudo chown -R www-data:www-data /var/www/yourwebsite.com
Note, these are just recommended Directory and File Permissions for WordPress.
Configure NGINX To Serve WordPress
Now that we have WordPress installed, we need to configure NGINX to serve our WordPress site.
sudo vim /etc/nginx/sites-available/yourwebsite.com
Add the following configuration:
server {
listen 80;
listen [::]:80;
server_name yourwebsite.com www.yourwebsite.com;
root /var/www/yourwebsite.com;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
}
}
- Save the file and exit.
- sudo ln -s /etc/nginx/sites-available/yourwebsite.com /etc/nginx/sites-enabled/
- sudo nginx -t to check for any syntax errors.
- sudo systemctl restart nginx
Install SSL Certificate Using Let’s Encrypt
To secure our WordPress site, we’ll install an SSL certificate using Let’s Encrypt and Certbot.
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourwebsite.com -d www.yourwebsite.com
Follow the prompts to obtain and install the SSL certificate.
Configure WordPress Permalink Structure
By default, WordPress uses a plain permalink structure. Let’s change it to a more SEO-friendly structure.
- Log in to your WordPress admin dashboard.
- Go to Settings > Permalinks.
- Select the “Post name” option and click “Save Changes”.
Set Up Server-Side Caching with NGINX
To improve the performance of your WordPress site, you can configure server-side caching using NGINX’s FastCGI cache module. This will cache the dynamic PHP content and serve it directly from NGINX, reducing the load on your server.
First, create a directory to store the cache files:
sudo mkdir -p /var/cache/nginx/wordpress
Open the NGINX configuration file for your WordPress site:
sudo vim /etc/nginx/sites-available/yourwebsite.com
Update The Nginx Conf file:
Update the configuration to include the caching directives:
fastcgi_cache_path /var/cache/nginx/wordpress levels=1:2 keys_zone=wordpress:10m max_size=10g inactive=60m use_temp_path=off;
server {
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
# FastCGI cache settings
fastcgi_cache wordpress;
fastcgi_cache_valid 200 60m;
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_cache_min_uses 1;
fastcgi_cache_lock on;
add_header X-FastCGI-Cache $upstream_cache_status;
}
}
fastcgi_cache_path: defines the cache directory, levels, keys_zone, max_size, and inactive time
fastcgi_cache: enables FastCGI cache for the specifized zone; in this case
wordpress
fastcgi_cache_valid: sets the caching time for successful responses. In this case we are setting it to 60 minutes for 200 codes.
fastcgi_cache_use_stale: In case there is an error, this will serve stale cache content
fastcgi_cache_min_uses: sets the minimum number of requests before a response is cached
fastcgi_cache_lock: this enables cache locking to prevent multiple requests from generating the same content (race conditions)
sudo nginx -t
sudo systemctl reload nginx
Don’t forget to cache static assets
GTMetrix will most definitely have issue if you don’t do this in your nginx config:
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff|woff2|ttf|svg|eot)$ {
expires 28d;
add_header Cache-Control "public, no-transform";
# Optionally set:
add_header Pragma "public";
add_header Vary "Accept-Encoding";
}
Updating PHP.INI For Wordpress File Uploads & Post Requests
php --ini
- Look for the loaded configuration file which in my case is
/etc/php/.3/cli/php.ini
sudo vim /etc/php/8.3/cli.php.ini
upload_max_filesize = 1048M
This can be whatever you need it to bepost_max_size = 1048M
This can be whatever you need it to be- Save the buffer
sudo systemctl reload php8.3-fpm.server
Pay attention to what your php version for this.
Add PHP workers
sudo vim /etc/php/8.3/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
Note: these can be adjusted as needed. I would say mind your actual cpu core/thread count for this. These are just example values.
sudo systemctl restart php8.3-fpm
Automatic Updates with WP-CLI and Crontab
WP Engine charges $7 usd per site for this :-)
wp --info
sudo crontab -e
orsudo crontab -u www-data -e
use www-data to act on behalf of the web server user.- get path to wp cli:
which wp
0 2 * * * /usr/local/bin/wp plugin update --all --path=/path/to/wordpress --allow-root > /dev/null 2>&1
Conclusion
Congratulations! You have successfully set up a self-hosted WordPress site on your own virtual private server. By following this tutorial, you have gained control over your website, improved security, and learned valuable Linux skills along the way.
Remember to keep your WordPress site, plugins, and themes up to date to ensure optimal performance and security. Regularly backup your site to protect your data in case of any unforeseen issues.
Self-hosting WordPress may require more effort compared to using managed hosting solutions, but the benefits of ownership, flexibility, and cost savings make it a worthwhile endeavor for many website owners.
Happy self-hosting!