This article assumes the reader has some basic familiarity with the Linux operating system, and it assumes that either Linux or Windows with WSL is in use on the reader's computer.
These days, everything is online. If you want to create your own site, and you do not know where to start, let me show ya a way to host your site on your own. You could spin up a website at your house, but more common is just to grab a Linode VPS for five dollars per month.
Websites run on servers, right? A server is just a computer. It’s a computer like any other. You could turn an old smart phone into a server if you so chose (I’ve done it, it was fun). The primary difference between a server and any other computer is the software that runs on it. Now, most companies that are in the business of providing web servers use slightly different hardware. First, the physical box that the hardware sits in is designed to be mounted in a rack with thousands of the computer’s closest friends. Second, the computers typically use different processors that are many more cores with lower clocks. Third, most have redundant network links, redundant power, and cooling that is designed to go from front to back across the computer (due to hot isle/cold isle setups in the datacenters).
In our case, we will use a VPS. A VPS is a virtual private server. Virtual servers are just virtual machines. When you emulate a game boy or other old gaming console on a computer, you are using a virtual machine. When you use a VDM (virtual dos machine) to run DOS programs on more modern Windows, you are using a virtual machine. A virtual server differs in that it can use resources on multiple physical machines, but otherwise it’s just another virtual machine, an emulated computer.
So, make your account, and select a five dollar per month VPS running Ubuntu. Some people may claim that WordPress requires more, but I am here to tell you that that is not so. With an appropriate configuration on the server, you can serve thousands of impressions in a very short time span with the smallest Linode VPS. We will get into the details of how a little later. Please take note of the password that is provided after the creation of your Linode VPS. You will need it. Also take note of the IP addresses that Linode assigns the VPS. You will need those.
The next thing you need to consider is your domain name. I prefer to use Gandi as my registrar. You want to create an account, and then search for your domain. If it’s available, then you go ahead and purchase the domain. After purchase, we need to create some DNS records.
We will want four total DNS records: two A records, two AAAA records. An A record says that a domain is assigned a certain IPv4 address, while AAAA says that a domain is assigned a certain IPv6 address. These records are required for your website to be accessed. The reason we are making four total records is for what is known as ‘@’ and then ‘www’.
The records should then appear as something similar to:
@ A 1800 1.1.1.1
@ AAAA 1800 2606:4700:4700::1111
www A 1800 1.1.1.1
www AAAA 1800 2606:4700:4700::1111
Of course, your IP addresses should be different as those listed are going to CloudFlare’s public DNS servers. With these things solved, we can now start some of the bigger work. We will be connecting to your server via SSH. You will connect with something like:
ssh [email protected]
You will be prompted for the password. The very first thing we are going to do is create a new user:
useradd username -u9000 -g100 -G 33,27,4 -d /home/username -s /bin/bash -m
Now, we need to set that new user’s password:
passwd username
If you are not proficient with vim, you can use An Introduction to VIm as a reference.
apt install vim -y
Now, go ahead and set that as the editor for Ubuntu
sudo update-alternatives --config editor
Before we disconnect and login with this user, we need to make certain that this user is able to use sudo. To do that, use:
visudo
All you need to do is make certain that you have the following line in the sudoers file:
%sudo ALL=(ALL:ALL) ALL
As long as that line is present, you can press :q
to exit your editor. You can now disconnect ( ctl+d ). Our next step is to generate an SSH key. You do this with:
ssh-keygen
Once you’ve generated your key, you can type:
cat $HOME/.ssh/id_rsa.pub
Copy the output text. Next you want to do:
ssh [email protected]
Once connected we want to change a few things about the SSH server. First, we need to add that SSH key.
vim $HOME/.ssh/authorized_keys
Paste the text from id_rsa.pub in there, and then save and quit. Next let’s make a change to the server config:
sudo vim /etc/ssh/sshd_config
You want to comment out (make the line start with #):
PermitRootLogin yes
PasswordAuthentication yes
and add:
PermitRootLogin no
PasswordAuthentication no
Then restart the SSH server:
sudo systemctl restart ssh
After this is complete, we want to make sure we have a firewall in place:
sudo ufw status numbered
If you get a message saying it isn’t enabled, do:
sudo ufw enable
Next, just enable some services in the firewall:
sudo ufw allow ssh; sudo ufw allow http; sudo ufw allow https; sudo ufw allow from 127.0.0.1/32
Then we reload to make these rules active:
sudo ufw reload
From this point, our initial prep work is complete. We now want to make sure that the software currently running on the server is up to date:
sudo apt update; sudo apt full-upgrade -y && sudo systemctl reboot
You will be disconnected due to the reboot, so reconnect, and then we are ready for some software installations:
sudo apt install nginx php-fpm php-mysql git build-essential mariadb-server letsencrypt proftpd -y;
We will be using WPM for the WordPress installation, grab the latest from here:
https://git.absurd.wtf/Absurd/WPM
Most of the time, the server software should be automatically enabled in systemd, but it isn’t always:
sudo systemctl enable nginx && sudo systemctl enable php7.2-fpm && sudo systemctl enable mysql && sudo systemctl enable proftpd
So, what are these software packages? Nginx is the webserver software. It outputs stuff based on web requests. PHP is the programming language that WordPress is written in, and it needs to be present because the webserver cannot understand PHP on its own. MariaDB is the database software in which the WordPress data will be stored. WPM is the WordPress Mangler (or Manager).
We now need to start the config process for a our website.
sudo vim /etc/nginx/sites-available/domain.com.conf
server {
listen 80;
listen [::]:80;
server_name domain.com www.domain.com;
root /var/www/domain.com;
location ~ /.well-known { default_type text/plain; allow all; }
location ^~ / { return 301 https://$host$request_uri; }
access_log off; error_log off; log_not_found off;
}
With the configuration in place, we can now enable this configuration.
sudo ln -s /etc/nginx/sites-available/domain.com.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo nginx -s reload
In the past, that configuration would have looked a bit different because we wouldn't necessarily worry about TLS. These days, we most certainly do. To get our certificate, we will use letsencrypt.
sudo certbot certonly --webroot -w /var/www/domain.com -d domain.com -d www.domain.com --dry-run
If that reports issues, it will most likely be either a problem with what we've done so far, or with the DNS records not yet being propogated. Debug or wait as appropriate. Once it is passing, do it for real:
sudo certbot certonly --webroot -w /var/www/domain.com -d domain.com -d www.domain.com
We now need to make sure that our TLS cert gets renewed:
crontab -e
0 0 * * * certbot renew; nginx -s reload
With our TLS certificate in place, let us now create our TLS configuration in nginx:
sudo vim /etc/nginx/sites-enabled/domain.com.conf
fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=WORDPRESS:512m inactive=1440m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie ;
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name domain.com www.domain.com;
root /var/www/domain.com;
client_body_buffer_size 128k;
client_max_body_size 128m;
ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
ssl_protocols TLSv1.2;
ssl_prefer_server_ciphers Off;
ssl_ciphers ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES256:ECDH+AES128:!aNULL:!SHA1:!AESCCM;
index index.html index.htm index.php;
charset utf-8;
location ~ /\.(?!well-known).* { deny all; }
location ~* wp-config.php { deny all; }
if ($http_user_agent ~ WordPress) { return 444; }
location ~* /xmlrpc.php { deny all; access_log off; log_not_found off; }
location ~* /favicon.ico { access_log off; log_not_found off; }
location ~* /robots.txt { access_log off; log_not_found off; }
location ~* \.(eot|ttf|woff|woff2)$ { add_header Access-Control-Allow-Origin *; }
location ~* \.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf|html)$ { expires 2d; add_header Cache-Control "public, no-transform"; }
error_page 404 /index.php;
location / { try_files $uri $uri/ /index.php?$query_string; }
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/wp-login.php") { set $skip_cache 1; }
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") { set $skip_cache 1; }
location ~ \.php$ {
fastcgi_pass unix:/run/php/php7.2-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache_valid 200 45m;
fastcgi_cache WORDPRESS;
fastcgi_buffers 256 16k;
fastcgi_buffer_size 128k;
fastcgi_connect_timeout 5s;
fastcgi_send_timeout 10s;
fastcgi_read_timeout 10s;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
}
log_not_found off;
access_log /var/log/nginx/domain.com-access.log;
error_log /var/log/nginx/domain.com-error.log warn;
}
Now we need to activate this new nginx configuration.
sudo nginx -t && sudo nginx -s reload
Within php-fpm.conf, you should have:
[global]
pid = /run/php/php7.2-fpm.pid
error_log = /var/log/php7.2-fpm.log
emergency_restart_threshold = 5
emergency_restart_interval = 1m
process_control_timeout = 10s
include=/etc/php/7.2/fpm/pool.d/*.conf
The nginx configuration we just put in place uses PHP. PHP needs some configuration as well.
sudo vim /etc/php/7.2/fpm/php.ini
Within the php.ini file, you specifically want to change:
memory_limit = 256M
post_max_size = 128M
upload_max_filesize = 128M
Those variables will allow you to do things like upload themes and whatnot. We are using FPM, and FPM uses a pool of workers, let's configure that as well.
sudo vim /etc/php/7.2/fpm/pool.d/www.conf
[www]
user = www-data
group = www-data
listen = /run/php/php7.2-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 128
pm.start_servers = 10
pm.min_spare_servers = 10
pm.max_spare_servers = 20
pm.max_requests = 0
listen.backlog = -1
request_terminate_timeout = 10s
rlimit_files = 131072
rlimit_core = unlimited
catch_workers_output = no
env[HOSTNAME] = $HOSTNAME
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
Now let's restart PHP-FPM to active that config:
sudo systemctl restart php7.2-fpm
Typically, this could be dangerous. The maximum number of workers is 256, while our memory limit is also 256. This means total memory use of PHP could reach 65.5GB which is way higher than 1GB of RAM on a tiny Linode. We, however, enabled PHP Fast CGI caching in Nginx. This means that each page should only be generated once every 10 minutes, and after that Nginx is just pushing the cached HTML. Now, let's make sure we can access MariaDB. This will put the client creds in place:
sudo cp /etc/mysql/debian.cnf ~/.my.cnf
Now just test that:
sudo mysql
Now, you can ctl+d to exit MariaDB. We haven't talked about on the programs installed earlier: ProFTPd. This is an FTP server. We will be using it as a local FTP server. Normally, FTP servers allow people to upload and download files from a server over the FTP protocol. We will not be using it that way. For us, this will be purely for WordPress's access to the filesystem.
sudo vim /etc/proftpd/proftpd.conf
Include /etc/proftpd/modules.conf
UseIPv6 off
IdentLookups off
ServerName "Debian"
ServerType standalone
DeferWelcome off
MultilineRFC2228 on
DefaultServer on
ShowSymlinks on
TimeoutNoTransfer 600
TimeoutStalled 600
TimeoutIdle 1200
DisplayLogin welcome.msg
DisplayChdir .message true
ListOptions "-l"
DenyFilter \*.*/
Port 21
MaxInstances 30
User proftpd
Group nogroup
Umask 022 022
AllowOverwrite on
TransferLog /var/log/proftpd/xferlog
SystemLog /var/log/proftpd/proftpd.log
<IfModule mod_quotatab.c>
QuotaEngine off
</IfModule>
<IfModule mod_ratio.c>
Ratios off
</IfModule>
<IfModule mod_delay.c>
DelayEngine on
</IfModule>
<IfModule mod_ctrls.c>
ControlsEngine off
</IfModule>
<IfModule mod_ctrls_admin.c>
AdminControlsEngine off
</IfModule>
Include /etc/proftpd/conf.d/
Now, let's just enable that:
sudo systemctl restart proftpd
Let's unarchive that copy of WPM we got earlier (adjust the version if needs be):
sudo tar xf wordpress_mangler_411.tar.xz
And, let's enter it's directory:
cd WPM/
To install we just do:
sudo make install && cd.. && sudo rm -rf WPM
Now, you should be able to execute:
sudo wpm --help
That should show you some output text. We are most concerned with -i
.
sudo wpm -i /var/www/domain.com
WordPress is now installed. If everything went according to plan, you should now be able to access the website portion of the install at https://domain.com
After you have completed the initial setup, it would be a good idea to run a backup:
sudo wpm -b -p /var/www/domain.com
If you desire the ability to check your WordPress install for malware in the future, you can do:
sudo apt install clamdscan -y
And then, we need maldet:
sudo -s
cd /usr/local/src/
wget http://www.rfxn.com/downloads/maldetect-current.tar.gz
tar -xzf maldetect-current.tar.gz
rm -f maldetect-current.tar.gz
cd maldetect-*
bash ./install.sh
ln -s /usr/local/maldetect/maldet /usr/local/bin/
Then ctl+d to exit the sudo session. With clamd and maldet installed, you can issue the WordPress verify command:
sudo wpm -v -m -s -b -r -p /var/www/domain.com
That's pretty much it! Be well!