Introduction
Linode provides affordable cloud compute, storage, and networking services. Their $5/month shared instance plan is a perfect starting point for a WordPress site. I got started with Linode because I wasn't happy with my hosting provider's declining performance and increasing price. Read on to learn how to host WordPress with Linode Marketplace Apps, configure an SSL certificate, upgrade PHP and other tips and tricks.
First Steps
There are a few things you will want to do before deploying your WordPress node:
- Create a Linode account if you don't already have one
- Purchase a domain name for your site
If you want to manage your domain DNS records at Linode, you will need to set your domain to use the Linode name servers. Additional information regarding the Linode DNS Manager can be found here.
Deploy WordPress Marketplace App
The Linode App Marketplace allows for rapid one-click deployment of popular applications. To deploy the WordPress app:
- Log in to the Linode Cloud Manager
- From the Linode dashboard, click Marketplace in the left-hand navigation menu
- Under the Select App section, select the "WordPress - Latest" app
- Scroll down to the "WordPress - Latest Options"
- E-Mail Address: Email address for the WordPress administrator account. Required.
- Admin Username: The user name for your WordPress admin account. This is the user name that you will use to log into your WordPress site. Required.
- Admin Password: The password for your WordPress admin account. Required.
- MySQL root Password: The root password for your MySQL database. Required.
- WordPress Database Password: The root password for your WordPress database. Required.
For security, it is recommended that all passwords are different!
Now for the Advanced Options. These are all preset and optional to configure.
- WebSite Title: Enter a name for your WordPress site. The default for this option is "My WordPress Site". For example if you want your web site to be called "The World's Greatest Blog", enter that here.
- The limited sudo user to be created for the Linode: This is a recommended setting. If you do not configure a limited sudo user account, then you will be using the root account for SSH access.
- The password for the limited sudo user: If you are creating a limited sudo user, enter the password here. Must meet strong password requirements.
- The SSH Public Key that will be used to access the Linode: If you wish to access SSH via Public Key instead of by password, enter the public key here. This public key will be used by the limited sudo user.
- Disable root access over SSH: The default is NO. If you create a limited sudo user, suggest setting this to YES to block root access via SSH.
- The following advanced options are only relevant if you are using Linode to manage your domain DNS records:
- Your Linode API token: If this is provided along with the subdomain and domain fields, the installation attempts to create DNS records via the Linode API.
- Subdomain: The subdomain for the DNS record. If your domain is example.com, entering www here would create a DNS record for www.example.com.
- Domain: The domain for the DNS record. If your domain is example.com, then enter example.com here.
- Would you like to be able to send password reset emails for WordPress: I'm not 100% sure as I could not find any documentation on this setting, but I believe this will create MX records for your domain. The default is NO.
- Would you like to use a free Let's Encrypt SSL certificate: Select Yes if you would like the install to create an SSL certificate for you, or No if you do not. The default is NO.
- Select an Image: This one is easy, as Debian 10 is the only image you can select.
- Region: Select where you want your Linode to reside. In general, you should select a region that is closest to you. For example, if you live on the west coast of the United States, select Fremont, CA.
- Linode Plan: Select your Linode's hardware resources. WordPress is very lightweight, so the Nanode 1GB plan should be more than sufficient. As your site grows, you can resize your Linode.
Now let's finish up the final remaining options.
- Linode Label: Enter a name for your Linode. It must be unique amongst all Linodes in your account. The default is Marketplace app name followed by region. In this example it's wordpress-latest-us-west. Required.
- Add Tags: You can create tags for your Linodes to group them together. Then, when you are logged into the Linode Cloud Manager you can group your Linodes by tag.
- Root Password: The primary administrative password for your Linode. This password must be provided when you log in to your Linode via SSH. Must meet strong password requirements.
- SSH Keys: If you have uploaded public keys to your Linode Profile, you can select which one to use, if any.
- Backups: For $2 per month you get automatic backups and the ability to take an on demand snapshot. That's a small price to pay for peace of mind. As it only takes a typo to bring your site down, I highly recommend this option!
- Private IP: Do NOT select this option. Giving your Linode a private IP will make it inaccessible from the public internet.
Now click the Create Linode button. Your Linode should be ready for use in about 5 minutes.
Access Your WordPress Linode
Once you click the Create Linode button you will be take to the Linode status display. You can also view the status of your Linode by clicking on Linodes in the left hand navigation menu and selecting the Linode you just created.
- Note the IPV4 address of your Linode.
- Copy and paste the IPV4 address into a new browser window or tab. You should see the home page of your WordPress site.
- Copy and paste the IPV4 address into your SSH client. You should be able to log into your Linode using either the sudo account (if created) or root account (if enabled).
Create DNS Records
If you are managing your own domain DNS records you should configure them now. At a minimum the IPV4 and IPV6 host records should be configured.
- Note the IPV4 and IPV6 addresses of your Linode.
- Add an IPV4 wild card A record to your DNS with a name of '@' pointing to your Linode IPV4 address.
- Add an IPV6 wild card AAAA record to your DNS with a name of '@' pointing to your Linode IPV6 address.
- If your web site has a subdomain such as www.example.com, add IPV4 A and IPV6 AAA records for the subdomain www.
If you plan to use your Linode to send e-mail or simply want your reverse DNS lookup to resolve to your domain name:
- Click Linodes in the left hand navigation menu and select the Linode you just created.
- Click Network.
- Under IP Addresses, select Edit RDNS and enter your domain name for both the IPV4 - Public and IPV6 - SLAAC addresses.
Configure SSH Public Key Authentication
If you did not specify any SSH Keys to use for public key authentication you can configure that now.
- SSH to your Linode IPV4 address. For example ssh [email protected] or [email protected].
- Login as root or the sudo account (if you created one during installation).
- Enter your password.
- mkdir ~/.ssh
- chmod 700 ~/.ssh
- vi ~/.ssh/authorized_keys
- Press I to enter insert mode.
- Paste the contents of your public key.
- If you are using putty or have issues pasting the contents of your public key, try entering paste mode by pressing escape, then type :set paste <enter> and then press I. The status bar should now say -- INSERT (paste) --. Then press the right mouse button to paste your public key.
- Enter :wq to save the file.
- chmod 600 ~/.ssh/authorized_keys
- exit
You should now be able to SSH into your Linode without having to type in a password.
Configure LetsEncrypt SSL Certificate
If you did not request a LetsEncrypt SSL certificate during installation or are managing your own DNS records, let's set that up now:
- SSH into your Linode.
- Make a backup copy of your Apache WordPress configuration file.
- cp /etc/apache2/sites-available/wordpress.conf /etc/apache2/sites-available/wordpress.conf.orig
- Edit /etc/apache2/sites-available/wordpress.conf with your favorite editor (vi, vim, nano, etc).
-
- Change ServerName from IP address to your domain name if necessary.
- If you are using a subdomain like www, add ServerAlias www.domain.name under the ServerName.
- If you ONLY want to respond to www.domain.name, make that the ServerName and do not add a ServerAlias.
- Install the CertBot Apache package:
- sudo apt install python-certbot-apache
- Verify your Apache configuration file is good:
- sudo apache2ctl configtest
- You should see "Syntax OK". If not, edit your config file or replace it from the backup (wordpress.conf.orig) and try again until you get a "Syntax OK" response.
- Reload Apache for the new configuration file to take effect:
- sudo systemctl reload apache2
- Start the CertBot Apache wizard:
- Enter an e-mail address to be used for renewal and security notices. LetsEncrypt certificates are only good for three (3) months, so make sure you enter a valid e-mail address so you can stay on top of renewing the certificate.
- Agree to the terms of service by pressing A <enter>.
- If you want to share your e-mail address with the Electronic Frontier Foundation, press Y <enter> otherwise press N <enter>.
- If you configured a ServerAlias in your WordPress config file, you will see both the ServerName and ServerAlias domains listed.
- Press enter to activate HTTPS for your domain(s).
- Determine if you want to redirect HTTP traffic to HTTPS:
- Enter 1 for no redirection
- Enter 2 to redirect all HTTP traffic to HTTPS
- Test your web site in a browser by going to https://your.domain.name and or https://www.your.domain.name.
- You can also test your HTTPS setup at SSL Labs.
Improve SSL Security
After setting up the LetsEncrypt SSL certificate, you will probably score a B at SSL Labs. To improve the security of your site and your score, two additional steps are required.
Create DNS CAA Record
Add a CAA wildcard record to your domain containing the following: 0 issue "letsencrypt.org"
Update Apache SSL Settings
- cd /etc/letsencrypt
- cp options-ssl-apache.conf options-ssl-apache.conf.orig
- vi options.ssl-apache.conf
- Find the following line: SSLProtocol all -SSLv2 -SSLv3
- Comment out the line by using a hash so that it looks like: #SSLProtocol all -SSLv2 -SSLv3
- Add a line under it which reads: SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
- Save the file
- You have now disabled TLS V 1.0 and TLS V 1.1
- Verify the Apache configuration
- sudo apache2ctl configtest
- Reload Apache
- sudo systemctl reload apache2
- Retest your SSL at SSL Labs
Update WordPress General Settings
If you access https://your.domain.name or https://www.your.domain name the site may look pretty messed up at this point. However, if you go to http://linode.ipv4.address the site looks fine. This is because Apache is now configured to handle requests for the actual domain name(s) rather than the IP address but WordPress is configured to expect the IP address. To reconfigure WordPress to expect the domain name:
- Open http://linode.ipv4.address/wp-admin in a browser.
- Log in with the WordPress admin user name and password specified during installation.
- Navigate to Settings > General from the left hand navigation menu.
- Chances are, the WordPress Address (URL) and Site Address (URL) are pointing to http://your.ipv4.address. If so, change both to https://your.domain.name or https://www.your.domain.name.
- You may also wish adjust other settings on this page such as site tagline and time zone.
- When finished, click Save Changes.
- Open https://(www.)your.domain.name in a browser. The site should come up without any warnings or errors.
Disable Search Indexing
While you are building your site, you probably don't want search engines trying to index it. Make the following changes to prevent search engines from indexing your site:
- Open https://(www.)your.domain.name/wp-admin in a browser and log in.
- Navigate to Settings > Reading from the left hand navigation menu.
- Check the "Discourage search engines from indexing this site" option to enable it.
- Click Save Changes.
- SSH to your Linode and log in.
- Navigate to /var/www/wordpress and create a robots.txt file:
- cd /var/www/wordpress
- touch robots.txt
- chown www-data robots.txt
- chgrp www-data robots.txt
- Edit robots.txt with your favorite editor (vi, vim, nano, etc) and add the following lines:
- User-agent: *
- Disallow: /
- Test and verify your robots.txt file at Technical SEO.
- Don't forget to enable search indexing when you are ready to go live!
Upgrade PHP
At the time of writing this guide, the Linode Marketplace WordPress App comes with PHP 7.3. I would suggest upgrading PHP to 7.4. WordPress Site Health (Tools > Site Health) will also complain, stating "Your site is running an older version of PHP". SSH into your Linode and perform the following steps to upgrade PHP to 7.4:
sudo apt install lsb-release apt-transport-https ca-certificates
# Download the signing key
sudo wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
# Add repo
echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php.list
# Update the list of available packages
sudo apt update
# Install PHP 7.4 and packages
sudo apt install -y php7.4 php7.4-common php7.4-xml php7.4-mysql php7.4-pdo php7.4-phar php7.4-simplexml php7.4-curl php7.4-mbstring php7.4-imagick php7.4-zip php7.4-gd php7.4-tidy php7.4-xsl
# Check version
php -v
# Disable PHP 7.3 and enable PHP 7.4 for Apache web server
sudo a2dismod php7.3
sudo a2enmod php7.4
sudo systemctl restart apache2
Log into your WordPress site and check Tools > Site Health. The PHP warning should now be gone.
Verify Zend OpCache
The Linode Marketplace WordPress app comes with the Zend OpCache preinstalled. Perform the following steps to ensure it's running:
- SSH into your Linode.
- Add the open source OpCache page:
- sudo curl -o /var/www/wordpress/opcache.php https://raw.githubusercontent.com/amnuts/opcache-gui/master/index.php
- Browse to https://(www.)your.domain.name/opcache.php and review the results
- When satisfied, remove the OpCache page:
- sudo rm /var/www/wordpress/opcache.php
Optimize PHP with MPM EVENT and PHP-FPM
Using PHP-FPM and MPM Event in an Apache environment decreases the web site page loading time and allows the web server to handle more concurrent connections. The default Apache Multi-Processing Module (MPM) is MPM prefork.
The prefork MPM causes Apache to fork into additional processes before the system receives requests. When the server receives requests, it responds quickly to each request because an Apache process already exists to handle it. If a problem occurs with an individual process, the system can kill the process with no effect on the other Apache processes.
Because the prefork MPM is not threaded, each child process can only handle one request at a time. The system queues concurrent requests and the MPM waits to process each request until the system becomes available. These scaled child processes may use of a large amount of system RAM.
The worker MPM causes Apache to fork into several processes so that a single crash does not kill the entire Apache process. Each forked process creates numerous threads, and a listener thread that listens for connections. This enables the system to serve multiple requests concurrently.
The event MPM serves a single HTTP request across multiple threads. However, the event MPM uses a dedication system and dedicates a thread to handle all live connections. The system allocates a request to the child threads only when it receives a request. This allows the threads instant availability after the request finishes.
Change the Multi-Processing Module
# Stop Apache service
systemctl stop apache2
# Disable PHP 7.4 module and prefork MPM
a2dismod php7.4
a2dismod mpm_prefork
# Enable event MPM
a2enmod mpm_event
Configure Apache to Use the FastCGI Process Manager
# Install PHP-FPM
sudo apt install php7.4-fpm
# Install FastCGID
sudo apt install libapache2-mod-fcgid
# Enable PHP-FPM, proxy and FastCGI proxy modules
sudo a2enconf php7.4-fpm
sudo a2enmod proxy
sudo a2enmod proxy_fcgi
# Restart Apache
sudo systemctl restart apache2
# Verify the active MPM module
sudo apachectl -M | grep 'mpm'
# You should get the following output
mpm_event_module (shared)
# Verify proxy and FastCGI proxy modules
sudo apachectl -M | grep 'proxy'
# Should get the following output:
proxy_module (shared)
proxy_fcgi_module (shared)
Verify FastCGI Process Manager
Apache is now configured to use the FastCGI Process Manager. Verify PHP is using the FastCGI Process Manager:
- Create an info.php file inside the WordPress root directory
- sudo vi /var/www/wordpress/info.php
- Add the following line:
- <?php phpinfo(); ?>
- Save and close the file
Open your web browser and navigate to http://your-server-ip/info.php. You should see a PHP info page. Check the "Server API" setting and confirm that "FPM/FastCGI" is listed.
Increase PHP Limits
The default PHP settings are a little low. Edit the PHP.INI file to increase them.
sudo vi/etc/php/7.4/fpm/php.ini
# Locate upload_max_filesize. The default limit is 2 MB. Let's change it to 32 MB
#upload_max_filesize = 2M
upload_max_filesize = 32M
# Locate post_max_size. The default is 8 MB. Let's change it to 32 MB
#post_max_size = 8M
post_max_size = 32M
# Save and exit
# Restart FPM
sudo systemctl restart php7.4-fpm
Monitor with Linode Longview
You can view general performance of your Linode over the last 12 hours by viewing its analytics data:
- Log in to the Linode Cloud Manager
- From the Linode dashboard, click Linodes in the left-hand navigation menu
- Click your Linode name
- Information such as CPU, disk and network utilization will be displayed in the Analytics tab
Install Linode Longview for even more information such as swap utilization, RAM utilization, memory breakdown by process, Apache statistics, MySQL statistics and more.
Prepare Apache Web Server
Linode Longview can provide statistics regarding Apache. In order to enable this capability, perform the following steps:
cd /etc/apache2/sites-available
cp 000-default.conf longview.conf
vi longview.conf
# Add the following section inside the <VirtualHost *:80> section
<Location /server-status>
SetHandler server-status
Require local
</Location>
# Save and exit
sudo a2ensite longview.conf
Install Linode Longview
- Log in to the Linode Cloud Manager
- From the Linode dashboard, click Longview in the left-hand navigation menu
- Click Add Client
- Copy the curl command line
- Log into your Linode via SSH
- Paste the command line and press enter
- During installation, you may see the following pop up: "Longview has detected Apache running on this server but was unable to access the server status page. Would you like to attempt to automatically configure mod_status? This will require reloading Apache to enable"
- If you receive the above pop-up, select NO
- During installation, you may see the following pop up: "Unable to automatically configure MySQL plugin: Longview has detected MySQL running on this server but was unable to automatically configure the connection. To allow Longview to access your MySQL instance please run the following query: CREATE USER 'linode-longview'@'localhost' IDENTIFIED BY 'some_long_password';"
- If you receive the above pop-up, copy the CREATE USER command line and then press OK
Create LongView MySQL Account
If you received the MySQL plugin error while installing Longview, perform the following:
# Log into MySQL
sudo mysql -u root -p
# Enter your root MySQL password
# Enter the CREATE USER command that you copied during the Longview installation
CREATE USER 'linode-longview'@'localhost' IDENTIFIED BY 'some_really_long_secure_password';
FLUSH PRIVILEGES;
EXIT
#Edit /etc/linode/longview.d/MySQL.conf to include the same username and password you just added. It should look like the following:
sudo vi /etc/linode/longview.d/MySQL.conf
# The file should look like this:
#username root
#password example_password
username linode-longview
password some_really_long_secure_password
# Save and exit
# Restart Longview
sudo systemctl restart longview
Optimize MySQL
The following MySQL optimizations are recommended when running on the shared 1 GB Linode plan.
# Edit the MySQL config file
sudo vi /etc/mysql/my.cnf
# Add the following lines under [Client-server]
[mysqld]
tmp_table_size=64M
max_heap_table_size=64M
query_cache_size=64M
skip-name-resolve
# Save and exit
# Edit the Maria DB server config file
sudo vi /etc/mysql/mariadb.conf.d/50-server.cnf
# Locate the following line and comment it out. Otherwise, it will take precedence over the my.cnf file
# Change
query_cache_size = 16M
To this:
#query_cache_size = 16M
# Save and exit
# Restart mysqld
sudo systemctl restart mysqld
# The following command will dump all MySQL settings and options. Browse through the results and ensure the settings that were changed in the my.cnf file "took".
mysqld --help --verbose
Enable Apache Modules for Caching
By default, the Expires and Headers modules are not enabled. If you want to configure browser caching, perform the following:
a2enmod expires
a2enmod headers
sudo systemctl restart apache2
# Check loaded modules
sudo apachectl -M
You should see the following in the list:
expires_module (shared)
headers_module (shared)
Set Expires Headers
If using a plugin such as WP Rocket, the expires headers will be set automatically. To set expires headers, modify the .htaccess file. The following example settings were taken from GTmetrix:
<IfModule mod_expires.c>
ExpiresActive On
# Images
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/gif "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType image/x-icon "access plus 1 year"
# Video
ExpiresByType video/webm "access plus 1 year"
ExpiresByType video/mp4 "access plus 1 year"
ExpiresByType video/mpeg "access plus 1 year"
# Fonts
ExpiresByType font/ttf "access plus 1 year"
ExpiresByType font/otf "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType application/font-woff "access plus 1 year"
# CSS, JavaScript
ExpiresByType text/css "access plus 1 year"
ExpiresByType text/javascript "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
# Others
ExpiresByType application/pdf "access plus 1 year"
ExpiresByType image/vnd.microsoft.icon "access plus 1 year"
</IfModule>
I also had to add the image/webp type to the .htaccess file as follows:
# Add the WEBP Image Type
AddType image/webp .webp
Conclusion
Your WordPress Linode should now be up and running, properly configured and optimized. Happy hosting!