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:
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
- Create virtual host(s), assign them domain names.
- Create SSL certs via Letsencrypt and set-up those for use with the said domains.
- Enable mapping S3 bucket for regular backups.
- 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.