This article presumes you have a fresh Ubuntu 16.04 server and you can log into it. I recommend using SSH keys if you can, but that doesn’t affect this article.
Run the following commands to install PerconaDB:
wget https://repo.percona.com/apt/percona-release_0.1-4.$(lsb_release -sc)_all.deb dpkg -i percona-release_0.1-4.$(lsb_release -sc)_all.deb apt-get update apt-get upgrade apt-get install percona-server-server-5.7 -q -y
Note: The Percona installation will ask you for a root password. This is the root password for your database and nothing to do with the root password for your server. If you wish, leave the password blank and it will still be secure because remote logins will be disabled.
Install Nginx and PHP 7
Run the following to install Nginx and PHP 7:
apt-get install nginx -y apt-get install php -y apt-get install -y tmux curl wget nginx php7.0-fpm php7.0-cli php7.0-curl php7.0-gd php7.0-intl apt-get install -y php7.0-mysql php-memcached php7.0-mbstring php7.0-zip php7.0-xml # now we need to temporarily set up nginx to work over HTTP so we can install WordPress and create SSL certificates git clone https://github.com/dhilditch/wordpress-rocket-stack /root/wordpress-rocket-stack/ cp /root/wordpress-rocket-stack/etc/nginx/* -R /etc/nginx/ rm /etc/nginx/sites-enabled/default.conf ln -s /etc/nginx/sites-available/install.wordpress.rocketstack.conf /etc/nginx/sites-enabled/ wget https://wordpress.org/latest.zip -P /var/www/ apt-get install unzip unzip /var/www/latest.zip -d /var/www/ mv /var/www/wordpress /var/www/rocketstack chown www-data:www-data /var/www/rocketstack -R rm /var/www/latest.zip
#if we are on a single CPU host, run the following mkdir /etc/systemd/system/nginx.service.d printf "[Service]\nExecStartPost=/bin/sleep 0.1\n" > /etc/systemd/system/nginx.service.d/override.conf systemctl daemon-reload service nginx restart
Ok – so now point your domain name at the IP address of your server, and when you visit you should see the WordPress installation screen.
We need a database, and we also would like SSL for *after* WordPress installation – unfortunately, installing WordPress using SSL is buggy, but it’s easy to switch AFTER installation.
Connect to mysql using:
Or if you entered a password for root, you’ll need to connect using this:
mysql -u root -P
Then run the following SQL:
create database rocketstack; grant all privileges on rocketstack.* to rs@localhost identified by 'CHOOSEASTRONGPASSWORD';
Visit your domain URL through a browser and you’ll see the WordPress installation screen. Choose your language, then the 2nd screen will ask you for the database name, database user name, and database password – use the strong password you used in the last line.
Once you have entered those details, continue through to finish your WordPress installation. Once done, we will install SSL and the rest of the components.
Install SSL using Letsencrypt
Letsencrypt gives you free SSL certificates and I’ve simplified the scripting of this for you.
git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt mkdir /var/www/acme/ chown www-data:www-data /var/www/acme
Edit /etc/nginx/snippets/acme-challenge.conf and replace yourdomain.com with your actual domain – this is on one line, and that line looks like this:
return 307 $scheme://acme.yourdomain.com$request_uri?redirect=yes;
So, if for example you have the domain wpintense.com, it would become:
return 307 $scheme://acme.wpintense.com$request_uri?redirect=yes;
service nginx restart /opt/letsencrypt/letsencrypt-auto certonly -a webroot --webroot-path=/var/www/acme/ -d yourdomain.com -d www.yourdomain.com --agree-tos
0 5 1 * * /opt/letsencrypt/letsencrypt-auto certonly -a webroot --webroot-path=/var/www/acme/ -d yourdomain.com -d www.yourdomain.com --renew-by-default
Now, we want to disable the temporary nginx conf file used and replace them with the production settings.
Edit the file /etc/nginx/sites-available/varnish.rocketstack.conf and replace the 2 SSL lines with the correct location of your newly generated SSL files – this involves simply replacing yourdomain.com on these 2 lines:
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
Once you have edited that file, we will remove the temporary nginx config and enable production config.
ln -s /etc/nginx/sites-available/wordpress.rocketstack.conf /etc/nginx/sites-enabled/ ln -s /etc/nginx/sites-available/varnish.rocketstack.conf /etc/nginx/sites-enabled/ rm /etc/nginx/sites-enabled/install.wordpress.rocketstack.conf service nginx restart
Install and configure Varnish
apt-get install varnish cp /root/wordpress-rocket-stack/etc/varnish/default.vcl /etc/varnish/default.vcl service varnish restart
apt-get install redis-server apt-get install php-redis
maxmemory 128mb maxmemory-policy allkeys-lru
service redis-server restart service php7.0-fpm restart
Optimising PerconaDB Config
[mysql] key_buffer = 512M max_allowed_packet = 512M thread_stack = 192K thread_cache_size = 8 query_cache_limit = 512M query_cache_size = 4192M (or 0 can improve performance, it depends) innodb_buffer_pool_size = 20000M (according to your memory - as a guide, use 60% of your memory here) innodb_buffer_pool_instances = 16 (according to how many cores you have) innodb_io_capacity = 5000 (because Digital Ocean use fast SSDs so we don't want InnoDB to be limited)
[mysql] key_buffer = 51M max_allowed_packet = 51M thread_stack = 19K thread_cache_size = 8 query_cache_limit = 51M query_cache_size = 419M innodb_buffer_pool_size = 2000M innodb_buffer_pool_instances = 16 innodb_io_capacity = 5000
Installing and configuring fail2ban
sudo apt-get install fail2ban cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Create /etc/fail2ban/filter.d/wordpress.conf and add the following lines:
[Definition] failregex = ^.*POST.*(wp-login\.php|xmlrpc\.php).* (403|499)
Then edit /etc/fail2ban/jail.conf:
Change bantime at top to 86400 (1 day instead of 10 minutes)
Find [nginx-http-auth] and add below it:
So, it should look something like this:
[nginx-http-auth] enabled = true filter = nginx-http-auth port = http,https logpath = /var/log/nginx/error.log
Add the following after the nginx section in the jail.conf:
[wordpress] enabled = true port = http,https filter = wordpress logpath = /var/log/nginx/*access.log maxretry = 10
Then restart fail2ban with:
service fail2ban restart
Installing and configuring W3 Total Cache
I’ve tried all of the caching plugins, but the free version of W3 Total Cache is the best – just be sure to avoid using it to compress files (we already compress through Nginx) and don’t use their minification (if you want minification, use Cloudflare instead).
Here are the settings I tend to use:
Page caching: Disk Enhanced
Object caching: Redis
Browser Caching: Yes, then modify browser caching settings to enable adding the expires header and all other headers, but untick ‘compress’ files.
Reverse proxy: Tick the checkbox and enter 127.0.0.1 in the textarea so your local varnish cache can be purged.
The above configuration files include configuration to pass through the correct IP addresses from your visitors, even when using Cloudflare, meaning fail2ban will work properly, but there are still some Cloudflare settings to edit:
Caching -> Browser expiration -> change to 1 year
Caching -> Caching Level -> change to No Query String
Speed -> Autominify -> tick JS and CSS
Speed -> Rocket Loader -> change to automatic
Crypto -> SSL -> Change to FULL
Results with GTMetrix
The GTMetrix score, like all the page-speed analysers (Pingdom etc) doesn’t actually take server-response-speed into account in their score. That might sound odd, and it is.
The speed that matters the MOST for your website being able to scale is the TTFB – time to first byte. It’s important that uncached this stays below 500ms. You can install the Query Monitor plugin and you’ll see the uncached TTFB along your admin bar.
Running GTMetrix over a site I installed the above stack onto, I get the following results:
Note: you have to click the ‘Waterfall’ tab to view the TTFB speed:
Latest posts by Dave Hilditch (see all)
- Welcome Freddy to the team - February 21, 2018
- Why the WooCommerce Products On Sale shortcode sucks (including a free plugin to fix it) - February 7, 2018
- External Images Update Released – manual update required - January 22, 2018