Ultimate Guide — Installing WordPress On EC2 With Latest Apache Event MPM, HTTP2, PHP7, Fedora, Memcached, Pagespeed, Mariadb & Selinux 2017

Well the title says I’m serious about it. Memcache / Mod_Pagespeed mean that it’s built to perform, PHP7 / Fedora means it’s hot out of the oven and Selinux means it’s tight and secure.

This is not another tutorial to get WordPress working with all the security holes and sluggishness. It’s going to be a production server. Start, shall we?

For the purpose of using this as a production server, here’s the configuration that I’ve gone with:

Fedora Cloud 25

Fedora strives to embrace new technology first before it gets introduced to other distros. And it has a six-month release schedule — a perfect between updates and stability. See 5 Reasons to Use Pure Open Source Distro, Fedora.

Apache but with Event MPM

Apache is the de facto web server. There are other alternatives which claim fast and lightening speeds. But that’s debatable. Apache has made technological breakthroughs. With the Event MPM Apache has kept up with the growing requirements and expectations. And it still remains my personal favorite. Please also see:

Stop using PHP-FPM to argue using Nginx vs Apache

Mariadb—the sql server

An drop-in alternative to MySQL ever-since MySQL was acquired by Oracle.

PHP-FPM 7

Apache configured with the Event MPM required PHP-FPM. We’ll use the latest and greatest—version 7. Fore further reading: https://wiki.mikejung.biz/Apache#Event_MPM

Memcached—Our caching solution

Memcached is a general-purpose distributed memory caching system. It is used to speed up dynamic database-driven websites by caching data and objects in RAM to reduce the number of times an external data source.

Mod Pagespeed from Google

Mod Pagespeed is an Apache (also Nginx) module which is open-source. It optimize your site serving at server level. Think Pagespeed Insights.

Letsencrypt for SSL

An SSL website not only provides better security / encryption for browser-server communication but also speeds up your site with mod_http2—the HTTP2 protocol and even counts as an SEO signal for ranking boots. Kills two birds with one stone plus one bonus 😉 Further reading: https://www.converticacommerce.com/wordpress-consulting/7-easy-steps-to-implement-ssl-https-on-your-wordpress-website-in-2017/

That should get us a server functional enough to install WordPress. Of course we’d use W3TC.

Performance tuning

We’ll use htop, iftop, httperf and mysqltuner commands for load-testing and fine-tuning.

Why not XXX cache / another best web-server?

Well, we could discuss and argue about them all day. But let’s see the context: This is a LAMP setup for WordPress. If you think some package or application will give your breakthrough speeds, it’s going to be purely academic and for bench-marking purposes only. In real-world, practical situations all this speed gain is largely decided by industry best-practices. So a bloated vs light weight install of WordPress would make more of a dent in the speed and performance that x-web-server vs y-web-server.

If you don’t have an AWS account, today is a fine day to create one. Once you have the account and are logged into http://console.aws.amazon.com, navigate to https://cloud.fedoraproject.org/ and under Fedora 25 Cloud Base Images for Amazon Public Cloud find GP2 HVM AMIs and click to launch.

This will walk you through the process of creating an EC2 instance.

Creating & Launching the EC2 Instance

Installing Fedora Cloud 25

Generally the majority of sites use the Ubuntu or the Amazon Linux (which is Amazon’s version of CentOS). Fedora 25 belongs to the second group and changes in Fedora trickle into Red Hat, CentOS and finally Amazon Linux. To install Fedora Cloud 25 just head to https://cloud.fedoraproject.org/ and scroll down to the section
Fedora 25 Cloud Base Images for Amazon Public Cloud. Click on “Click to Launch”.

Installing httpd, php-fpm, mariadb, phpmyadmin, mod_pagespeed, memcached, letsencrypt

The mega command: This will install the required packages.

sudo dnf install httpd php-fpm mariadb mariadb-server memcached nc php-pecl-apcu php-pecl-igbinary php-zip php-bcmath php-pecl-msgpack php-soap php-tidy php-xmlrpc php-zip php-cli php-pear php-pdo php-mysqlnd php-pgsql php-pecl-mongodb php-pecl-memcache php-pecl-memcached php-gd php-mbstring php-mcrypt php-xml vim htop wget at letsencrypt git python-certbot-apache zip php-pecl-igbinary php-zip php-bcmath php-pecl-msgpack php-soap php-tidy php-xmlrpc php-zip mysqltuner iftop httperf --best --allowerasing

We need the following services to start automatically: httpd, mariadb, memcached, php-fpm. So issue these commands in order:

sudo systemctl enable mariadb
sudo systemctl enable php-fpm
sudo systemctl enable memcached
sudo systemctl enable httpd

Of course this wouldn’t start these services until the next reboot. So for now feel free to start them manually:

sudo service mariadb start
sudo service php-fpm start
sudo service memcached start
sudo service httpd start

PHP Configuration

Before we start using PHP, we need to make certain configuration changes. The only issues I typically  encounter on freshly installed servers is that the post_max_size and upload_max_filesize are set a bit too low. So let’s change those.

Edit the file /etc/php.ini and change the value of post_max_size and upload_max_filesize to 64M. That’s a decent figure.

Also find this line, un-comment it and set a default value depending your time zone.

date.timezone = "America/New_York"

Install PhpMyAdmin

PhpMyAdmin will allow you to administer the sql databases via a web-front-end instead of logging in via ssh and issuing sql commands.

sudo dnf install phpmyadmin --best --allowerasing

PhpMyAdmin by default restricts the access to administration to the local host only. We need to access it from our system. Unless you have a static IP address, here’s the way to change the config.

Edit /etc/httpd/conf.d/phpMyAdmin.conf. Under the <Directory /usr/share/phpMyAdmin/> and <Directory /usr/share/phpMyAdmin/setup/> sections, match them to this, else PHPMyAdmin will only alow local logins.

<RequireAny>
#    Require ip 127.0.0.1
#    Require ip ::1
    Require all granted
</RequireAny>

Making SQL Databases Crash-Proof

Innodb is more crash tolerant than MySAM. Let’s change the mariadb’s default storage-engine. Edit the file /etc/my.cnf and change the default database storage to innodb. Run WooCommerce on it and no problem.

Look for the string ‘default-storage-engine’ in /etc/my.cnf and change it to ‘innodb’.

[mysqld]
default-storage-engine = innodb

Installing Mod_Pagespeed

Let’s get on with it.

wget https://dl-ssl.google.com/dl/linux/direct/mod-pagespeed-stable_current_x86_64.rpm

sudo rpm -U mod-pagespeed-*.rpm

Verify all-good

sudo vim /var/www/html/index.php

Press the i key to insert and type in

<?php
phpinfo();

Press ‘escape’ to go out of the insert mode. Press :wq This will save the file and exit out of vim.

Navigate to http://<ec2-instance-ip>

This should give you the complete info of the php environment. Open up firebug, go to the network tab, reload the page and verify that you see a ‘X-Mod-Pagespeed’ header present in the response headers.

Securing the server

Most tutorials end here. Right now you only have one user ‘fedora’. And this guy has ‘sudo’ powers. You need another user that you’ll use for regular tasks for the role of a webmaster. This includes sftp or even a login shell that wouldn’t allow sudo powers to anyone. It’s a good practice to have a non-sudo user for routine tasks.

The second reason you do this is because WordPress is finicky about the ‘group’ and ‘owner’ of the files in the installation. If it’s not the same as the user under which the webserver is running then you’ll have to manually edit files and WordPress updates will prompt you for ftp credentials.

By default the httpd server on Fedora runs with ‘apache’ as the group and owner. We’ll turn this guy into a real user that has login access but no sudo powers.

Edit the /etc/passwd file

sudo vim /etc/passwd

Find this line:

apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin

It says that the home directory of the ‘apache’ user is /usr/share/httpd and the login shell is set to /sbin/nologin or the account does not have a login shell.

Change it to:

apache:x:48:48:Apache:/var/www:/bin/bash

Now let’s add real credentials for the ‘apache’ user.

sudo mkdir /var/www/.ssh    // Make the .ssh directory. We'll store the public key here
sudo chmod 700 /var/www/.ssh  // Change the permissions so no one else has access

sudo touch /var/www/.ssh/authorized_keys  // Create the authorized_keys file
sudo chmod 600 /var/www/.ssh/authorized_keys    // Set permissions so that only user 'apache' can read it
sudo chown -R apache:apache /var/www

Create a key on mac

ssh-keygen -t rsa -f apache.pem

Feel free to give it a password if you want tighter security and love to type.

This will save two files, one with a `.pub` extension. Copy the contents of this file to the server inside the authorized keys:
sudo echo ssh-rsa <abrakdabra> > /var/www/.ssh/authorized_keys

Note: If you have trouble logging in, try using the ssh-copy-id command to fix it.

Fix SWLinux else it won’t allow login

sudo semanage fcontext -at ssh_home_t /var/www/.ssh/    # set correct contexts for .ssh directory
sudo semanage fcontext -at ssh_home_t /var/www/.ssh/authorized_keys     # set correct contexts for .ssh/authorized_keys file
sudo restorecon -RvF /var/www/.ssh    # apply changes

Give selinux write access to httpd or else WordPress would crib “Sorry, but I can’t write the wp-config.php file.”

sudo semanage fcontext -a -t httpd_sys_rw_content_t /var/www/html
sudo semanage fcontext -a -t  httpd_sys_rw_content_t '/var/www/html(/.*)?'
sudo restorecon -RvF /var/www/html
Make sure user apache's bash profile looks like the standard bash prompt.
sudo cp ~/.bash_profile /var/www/
sudo cp ~/.bash_rc /var/www/

Set file permissions

sudo chmod 2775 /var/www/html
find /var/www/html -type d -exec sudo chmod 2775 {} ;
find /var/www/html -type f -exec sudo chmod 0664 {} ;

Try login:

ssh  -v -i key.pem apache@<ec2 ip address>

Follow the tutorial here to install WordPress:

`wget` WordPress — Oh Yeah! Install WordPress Via SSH

Performance Tuning

Performance tuning for the most part is a result of observing the load and tweaking. The mysqltuner command you can find out the recommended settings for mariadb server. You’ll need to edit /etc/my.ini and update it with the recommendations from mysqltuner. Between each iteration of mariadb tweaking I recommend that you leave it running for at least 48 hours before trying to assess performance and tweaking it again.

An exhaustive tuning of apache is covered in this video here:

Consolidating the left-overs

Want trouble? You got it

This much should do for a demo WordPress install. But we have things to do and words to keep and miles to go before we sleep.

We’re not done yet. There are some things to do before we can call it ready for production. Step-wise, we need to

  1. Create virtual host(s), assign them domain names.
  2. Create SSL certs via Letsencrypt and set-up those for use with the said domains.
  3. Enable mapping S3 bucket for regular backups.
  4. Enable swap partition.

Creating Virtual Hosts

Instead of redirecting automatically to port 443 (regular http to https), I prefer to let the files remain accessible on port 80 also. Automatic redirects have their benefits but also some ramifications.

<VirtualHost *:80>
    DocumentRoot "/var/www/html/prod/<directory>"
    ServerName example.com
    ServerAlias www.example.com
    include conf/my-vh-non-ssl.conf
</VirtualHost>

<VirtualHost *:443>
    DocumentRoot "/var/www/html/prod/<directory>"
    ServerName example.com
    ServerAlias www.example.com
    include conf/my-letsencrypt.conf
    <IfModule pagespeed_module>
        ModPagespeedLoadFromFile "https://www.example.com" "/var/www/html/prod/<directory>"
    </IfModule>
</VirtualHost>

Let’s configure the domain(s) to use letsencrypt certificates. Issue the following command:

sudo vim /etc/httpd/conf/my-letsencrypt.conf

Hit i to insert and then paste in:

SSLCertificateFile /etc/letsencrypt/live/addongenie.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/addongenie.com/privkey.pem
<Directory ~ "/.*/">
    Options -Indexes
</Directory>

Hit Esc : w q to save the file.

Another one. Issue the following command:
sudo vim /etc/httpd/conf/my-vh-non-ssl.conf

#Turn Off Indexes across all directories for the virtual-host.
<Directory ~ "/.*/">
    Options -Indexes
</Directory>

Some may wonder what it does. I think while we are configuring and setting everything up, it’s best to have a common file for non-ssl virtual-hosts and another one for ssl-virtual-hosts. You never know what common setting you may want to apply to all these virtual-hosts in the future.

Support for HTTP2 protocol

Just add this line at the top of your virtual hosts file to enable support for HTTP2 protocol:

Protocols h2 h2c http/1.1

Creating Let’s Encrypt SSL Certificates

You can issue this command and create an SSL certificate for multiple hosts in a single command. The configuration is already active and the same is stored in /etc/httpd/conf/my-letsencrypt.conf

sudo certbot certonly --webroot -w /var/www/html/prod/example.com -d example.com -d www.example.com -w /var/www/html/prod/example1.com -d example1.com -d www.example1.com

Just follow Step 8 in this article on configuring WordPress to use SSL.

Mapping S3 bucket for regular backups

Installing RioFS

RioFS is an userspace filesystem for Amazon S3 buckets for servers that run on Linux and MacOSX. It supports versioned and non-versioned buckets in all AWS regions. RioFS works as a storage backend for legacy daemons which cannot talk natively to S3. It handles buckets with many thousands of keys and highly concurrent access gracefully.

We’ll have to build it from source. So let’s install the dependencies:

sudo yum install automake fuse gcc-c++ glib2-devel fuse-devel libevent-devel libxml2-devel openssl-devel libcurl-devel make --best --allowerasing

Finally ready to fetch the source:

wget https://github.com/skoobe/riofs/archive/master.zip
unzip master.zip
cd riofs-master
./autogen.sh
./configure
make && make install

This compiles and installs the riofs command on the server. Let’s mount our S3 bucket. You’ll need to go to AWS S3 page and create yourself an S3-bucket. Also you’ll need to click on the menu at top left > My Security Credentials to get your AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY.

From the shell:

export AWS_ACCESS_KEY_ID=<AWS_ACCESS_KEY_ID>
export AWS_SECRET_ACCESS_KEY=<AWS_SECRET_ACCESS_KEY>
mkdir /mys3bucket #the mount point
sudo chmod 775 /mys3bucket # give permissions
riofs <bucket-name> /mys3bucket #mount the S3 bucket to the mount-point.

Feeling nerdy? Here’s my backup script which runs via cron and backs-up stuff.

#!/bin/sh

file=/var/www/html
if [ -e "$file" ]; then
HALIAS="Fedora-"
else
HALIAS="My-AWS-EC2-WP-Prod_Server"
fi

export TZ="America/New_York"
backuptime=$HALIAS$1-y$(date +%Y)-m$(date +%m)-d$(date +%d)-h$(date +%H)-m$(date +%M)-s$(date +%S)
mysqldump --user=aha --password='you-bet' --all-databases > /var/www/backup/out.sql
sudo tar -cvjf /var/www/backup/latest-www.tar.bz2 --directory=/var/www html > /var/www/backup/$backuptime.log
sudo tail /var/www/backup/out.sql >> /var/www/backup/$backuptime.log
sudo tar -cvjf /var/www/backup/latest-mysql.tar.bz2 --directory=/var/www/backup out.sql
sudo cp /var/www/backup/latest-www.tar.bz2  /mys3bucket/$backuptime-www.tar.bz2
sudo cp /var/www/backup/latest-mysql.tar.bz2  /mys3bucket/$backuptime-mysql.tar.bz2
echo size of db-backup: >> /var/www/backup/$backuptime.log
sudo du -sh /var/www/backup/latest-mysql.tar.bz2 >> /var/www/backup/$backuptime.log
echo size of files-backup: >> /var/www/backup/$backuptime.log
sudo du -sh /var/www/backup/latest-www.tar.bz2 >> /var/www/backup/$backuptime.log
sudo rm -vf /var/www/backup/out.sql
sudo rm -vf /var/www/backup/latest-www.tar.bz2
sudo rm -vf /var/www/backup/latest-mysql.tar.bz2
echo Backup Complete. Pls Verify!!!

Read through each line and update the code with your credentials, backup location etc.

Homework: Figure out how to set it up via cron.

Enable swap

I strongly recommend a swap partition. This not only gives the server some virtual memory, it also keeps the server from running out of RAM and from freezing.

Enabling swap requires you to create a new EC2 volume (4GB should do) and attach it to your running instance, formatting it with a file-system, setting it up as swap partition, enabling swap and finally enabling swap at startup. It’s fairly straight-forward and I’d point you to this great guide in favor of limiting the length of this article.

How to Add Swap Partition on EC2 Linux Instance

Now it’s time to make your server live. In the EC2 console, in the left sidebar, click on Elastic IPs. Click “Allocate New Address”. This will allocate a new IP address. Right-click on this IP address and assign it to your production server that we’ve just set-up.

Update DNS

Depending on who your DNS provider is, you’ll need to point your domain(s) to this IP and viola your site is live.

Finally: Some handy commands / scripts

# allow apache to send mail. Actually WP will use it for sending mails for 
# new user registrations, forgot-password mails etc.
sudo setsebool -P httpd_can_sendmail 1
# allow network connections for php to memcache (or so I remember from troubleshooting).
sudo setsebool -P httpd_can_network_connect 1 https://www.example.com/?ModPagespeedFilters=+debug

Here’s a neat script just in case I need to restart the webservices (also clears the memcached cache).
sudo vim ~/restartweb.sh
sudo chmod +775 ~/restartweb.sh

service mariadb restart
service php-fpm restart
echo 'flush_all' | nc localhost 11211
service memcached restart
service httpd restart

Refine this

All this I was able to self-learn from my past experience as a sysadmin (systems provisioning). But this certainly can be refined. For example a backup-script must not contain credentials, changing SSH to a different port etc. And there are several other improvements that can be done. Feel free to share and save this article so that you can come back to it later. You know where to find me.

Posted In:

Support & Maintenance, Code Snippets

3 Comments

  1. Great read! I ended up using https://restic.github.io for my backups to S3 instead of mounting etc. As for the pagespeed module, I like the free Cloudflare plan to handle CDN/caching/compression.

    Reply
  2. Thank you for your guide
    Can you post again the video of tunning apache,please?

    Reply

Comment on Ultimate Guide — Installing WordPress On EC2 With Latest Apache Event MPM, HTTP2, PHP7, Fedora, Memcached, Pagespeed, Mariadb & Selinux 2017

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