Hosting a WordPress blog on AWS (for free!)

Much of what I write is a compilation of what I found on the Internet, esp. Pēteris Ņikiforovs’s post. I am indebted to him, as I heavily benefitted from his post while trying to get WP working. However, since that article is a bit dated, and cannot be used verbatim — I decided to jot this post down.

It is assumed that one would be having the domain name handy (e.g. myblogsite.com or foobar.io), before proceeding with the installation. In this post, let’s go ahead with foobar.io.

So, foobar.io will be hosted on WordPress, with MySQL database, and PHP getting served via ngnix. To enable HTTPS access, we’d use a certificate generated via Letsencrypt.

On AWS, a t2.micro EC2 instance (free-tier eligible) would suffice for this set-up. Note that this instance is free only for 1yr, 750hrs a month, so, it would be a good idea to:

  1. Not run any other (EC2) instance in the same AWS account
  2. Create an AMI, once the installation is over, so that we can port our installation to a new AWS account after the 1yr period, and not have to do all this over again

Alright, so here we go!

I. Log-in to the instance, and start with creating environment variables, which’d used throughout the installation (Lines to modify are highlighted (optional), except. for line #1 (must-do!))

WP_DOMAIN="foobar.io" ## *** Change this domain *** 
WP_ADMIN_USERNAME="admin"
WP_ADMIN_PASSWORD="admin"
WP_ADMIN_EMAIL="no@spam.org"
WP_DB_NAME="wordpress"
WP_DB_USERNAME="wordpress"
WP_DB_PASSWORD="wordpress"
WP_PATH="/var/www/wordpress"
MYSQL_ROOT_PASSWORD="root"

II. Ensure that the environment is set (assuming that the above variables are put in a file called setEnv.sh)

source ./setEnv.sh

III. Install the software, viz. nginx, MySQL and PHP (but first remove the default apache server, and upgrade the packages)

sudo apt remove apache2 # we dont want the default server
sudo apt update && sudo apt upgrade -y # lets stay up to date
echo "mysql-server-5.7 mysql-server/root_password password $MYSQL_ROOT_PASSWORD" | sudo debconf-set-selections
echo "mysql-server-5.7 mysql-server/root_password_again password $MYSQL_ROOT_PASSWORD" | sudo debconf-set-selections
sudo apt install -y nginx php php-fpm php-mysql php-curl php-gd mysql-server

IV. Configure MySQL

mysql -u root -p$MYSQL_ROOT_PASSWORD <<EOF
CREATE USER '$WP_DB_USERNAME'@'localhost' IDENTIFIED BY '$WP_DB_PASSWORD';
CREATE DATABASE $WP_DB_NAME;
GRANT ALL ON $WP_DB_NAME.* TO '$WP_DB_USERNAME'@'localhost';
EOF

V. Configure nginx

sudo mkdir -p $WP_PATH/public $WP_PATH/logs
sudo tee /etc/nginx/sites-available/$WP_DOMAIN <<EOF

server {
  listen 80;
  server_name $WP_DOMAIN www.$WP_DOMAIN;
  root $WP_PATH/public;
  index index.php;
  access_log $WP_PATH/logs/access.log;
  error_log $WP_PATH/logs/error.log;
  location / {
   try_files \$uri \$uri/ /index.php?\$args;
  }
 location ~ \.php\$ {
 include snippets/fastcgi-php.conf;
 fastcgi_pass unix:/run/php/php7.2-fpm.sock;
 }
}
EOF

sudo ln -s /etc/nginx/sites-available/$WP_DOMAIN /etc/nginx/sites-enabled/$WP_DOMAIN
sudo systemctl restart nginx # if this fails, check the ngnix logs before continuing

VI. Install Letsencrypt, and auto-install certs (Note that in the DNS config, the domain should already be pointing to the current server public IP, as Letsencrypt would be performing domain validation)

cd ~
sudo apt install letsencrypt # we'll use it later, not right now
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
sudo ./letsencrypt-auto --nginx # respond to the prompts after this

VII. Finally, install WordPress

sudo rm -rf $WP_PATH/public/ # !!!
sudo mkdir -p $WP_PATH/public/
sudo chown -R $USER $WP_PATH/public/
cd $WP_PATH/public/

wget https://wordpress.org/latest.tar.gz
tar xf latest.tar.gz --strip-components=1
rm latest.tar.gz

mv wp-config-sample.php wp-config.php
sed -i s/database_name_here/$WP_DB_NAME/ wp-config.php
sed -i s/username_here/$WP_DB_USERNAME/ wp-config.php
sed -i s/password_here/$WP_DB_PASSWORD/ wp-config.php
echo "define('FS_METHOD', 'direct');" >> wp-config.php
sudo chown -R www-data:www-data $WP_PATH/public/

VIII. At this point we should be able to browse to foobar.io, to continue with the WP installation wizard, using the credentials we configured in the first step ($WP_ADMIN_USERNAME, $WP_USERNAME_PASSWORD).

IX. Since Letsencrypt certs expire after a given period of time, we might want to automate the renewal by creating a cron job

sudo tee /etc/cron.daily/letsencrypt <<EOF
letsencrypt renew --agree-tos && systemctl restart nginx
EOF
sudo chmod +x /etc/cron.daily/letsencrypt

That’s it! One should be all set now!

Troubleshooting:

If, for any reason, the WP password needs to be reset, the following can be done

mysql -u $WP_DB_USERNAME -p$MYSQL_ROOT_PASSWORD -d$WP_DB_NAME <<EOF
UPDATE `wp_users` SET `user_pass`= MD5('yourpassword') WHERE `user_login`='yourusername';
EOF