Setting Up An Amazon Web Services (AWS) Instance for WordPress: Part 2


This post is part two of a series on setting up an Amazon Web Service Instance for a WordPress site.  Part 1 covers the initial setup, security, and terminology of Amazon’s AWS.

Edited: Oct 12, 2016 to include newest versions of software.

If you’ve read the link above, you should have a newly created Amazon AWS account (running on Free Tier settings!) with an Ubuntu instance running and operational.  As it stands now, the instance is more like a brand new computer – there’s not much on it, and it really can’t do anything until we put some software on it.  This post covers the actual setup of Nginx, PHP(-FPM), MySQL, and a few other things we need to get the server up and serving websites.

Step 0: Update your System Packages

This should be a regular occurance for you as WordPress rarely sees conflict with the Linux system packages.

$ sudo apt-get update && sudo-apt-get upgrade

This command (minus the $ symbol, of course) will scan the package repositories for updates and will update them, prompting before the actual update runs. I like to reboot my system right after this, and you can do it from the command line:

$ sudo reboot

You’ll get kicked off, as you should since you just did a soft-reset, so ssh back in using the terminal:

$ ssh ubuntu@PUBLIC_HOSTNAME -i ~/.ssh/key-name.pem 

After this, your computer’s packages will be completely up to date. Now we can start installing our own.

Note: Linux uses a package management system to install software. Any dependencies – packages that are needed by other packages – should be installed with the commands we run. If you see a long list of packages being installed, don’t panic – that’s Linux making sure things are working correctly behind the scenes!

Step 1: Install Nginx

Nginx is, plain and simple, a web server. It routes incoming and outgoing connections to give people access to files, images, processed code, etc.  I like Nginx over Apache because the architecture is built to serve sites lean and fast. To install, run this from the command line:

$ sudo apt-get install nginx

We’re going to install the HTML5 Boilerplate’s Nginx Server Configs, which will take care of the heavy lifting of “tuning” our website out of the gate. These tweaks help with performance and security, so don’t skip out!

But, to do that, we need to install Git. Git is a version control system, and the files we need are stored on Github.

$ sudo apt-get install git
$ cd /etc
$ sudo mv nginx nginx-previous
$ sudo git clone nginx

This installs Git, changes to the /etc directory (where all of the misc. packages are installed), moves the initial config into a new folder, and brings down the boilerplate files into a default configuration folder.

Next, we want to tell Nginx what “user” it’ll be running information under. Personally, I like “www-data” since Ubuntu already has information built in for it:

$ cd /etc/nginx
$ sudo nano nginx.conf

Look around for a line that looks similar to this:

# Run as a less privileged user for security reasons.
user nginx nginx;

And change it to:

# Run as a less privileged user for security reasons.
user www-data www-data;

We’re going to go ahead and set up our website’s “virutal host” directory. This ensures that 1) our files all live in one specific place and 2) allows us later on to install more WordPress sites and run them on one server – neat!

$ sudo mkdir -p /sites/

Don’t put the www. in front of this – we’re just going for the root fully qualified domain name.

While we’re at it, let’s grab WordPress and go ahead and get it set up:

$ cd /sites/
$ sudo wget
$ sudo tar zxf latest.tar.gz
$ cd wordpress
$ sudo cp -rpf * ../
$ cd ..
$ sudo rm -rf wordpress/ latest.tar.gz

To walk through what we’re doing:

  • Changing into the directory we just created
  • Using WGET to “fetch” the latest version of WordPress
  • Using UnTar (similar to UnZip) to extract the files in the archive
  • Changing into the WordPress directory
  • Copying all of the files one folder lower (out of the WordPress directory)
  • Moving up to the root directory
  • Removing the wordpress folder (now empty) and the archive (not needed anymore).

We’ll come back here later once we’ve set up a few other things.

But first, we need to set this new directory up under the ownership of the nginx user:

$ sudo chown -R nginx:nginx /sites/

CHOWN (Change Ownership) does this easily enough, and we can hit every directory / file at once.

Back in the Nginx folder, we need to copy a few other configuration files from the old nginx configuration to the new one we just imported:

$ sudo cp /etc/nginx-previous/fastcgi_params /etc/nginx

This step is essential, as without it Nginx wouldn’t know how to run the PHP we’re installing later.

Each Nginx site has its own file that determines what happens when a user visits the site. We can set it up easily enough:

$ sudo nano /etc/nginx/sites-available/

This should bring up a blank text editor.

Note: Now would be the time that we introduce SSL certificates if we were using them. If you need them, great – I’ll do a follow up post that shows you how to install them easily.  For now, we’ll do a non-SSL site for ease of use.

Use this configuration to start things up:

# Define the microcache path.
fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=microcache:100m inactive=60m;

server {
 listen 80;
 listen [::]:80;

 root /sites/;
 index index.php index.html index.htm;


 # Include the basic h5bp config set
 include h5bp/basic.conf;

 location / {
     index index.php;
     try_files $uri $uri/ /index.php?$args;

 location ~ \.php$ {
     fastcgi_cache microcache;
     fastcgi_cache_key $scheme$host$request_method$request_uri;
     fastcgi_cache_valid 200 304 10m;
     fastcgi_cache_use_stale updating;
     fastcgi_max_temp_file_size 1M;
     fastcgi_index index.php;
     fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
     include fastcgi_params;

     # Local variables to track whether to serve a microcached page or not.
     set $no_cache_set 0;
     set $no_cache_get 0;

     # If a request comes in with a X-Nginx-Cache-Purge: 1 header, do not grab from cache
     # But note that we will still store to cache
     # We use this to proactively update items in the cache!
     if ( $http_x_nginx_cache_purge ) {
         set $no_cache_get 1;

     # If the user has a user logged-in cookie, circumvent the microcache.
     if ( $http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-postpass_" ) {
         set $no_cache_set 1;
         set $no_cache_get 1;

     # fastcgi_no_cache means "Do not store this proxy response in the cache"
     fastcgi_no_cache $no_cache_set;
     # fastcgi_cache_bypass means "Do not look in the cache for this request"
     fastcgi_cache_bypass $no_cache_get;

Suffice it to say there’s a LOT going on here, but this is the configuration for the site. It tells the server what to do when people visit, where to route connections, and so on.

Next, we have to “enable” the site by symlinking – creating a link relationship – to the “sites-enabled” folder in nginx:

sudo ln -s /etc/nginx/sites-available/ /etc/nginx/sites-enabled/

This activates the site, but the server is still not running. We want to set it to run upon server startup:

$ sudo update-rc.d nginx defaults

Step 2: Setting an IP Address

Our Amazon AWS server techincally doesn’t have a public IP address yet, but it’s simple enough to create one.

Log into the Amazon AWS Console and click on the EC2 Link. In the Instance Console, look on the left side for Elastic IP addresses. These IP addresses are technically static, but you can remap them instantly to other containers should the need arise.

Hit the Allocate New Address button, and confirm. A blue bar with the IP address will pop up. The top blank, Instance, should be clicked on. Your running instance will pop up. Select it, watch the values populate, and hit Associate (and confirm). Congrats! Your server now has a public facing IP address, which means we can have our domain point to it once we’re done!

Nginx should be ready to go now. Start things up:

$ sudo service nginx start

Step 3: Installing PHP 7

If Nginx is the nervous system, then PHP is the brains behind it. PHP serves up any non-markup elements – variables, functions, and computations – that come into play on the web… at least on our server, anyway.

$ sudo apt-get install php7.0-fpm

This will bring in all of the packages and dependencies you need. We need to change the default user, so enter in:

$ sudo nano /etc/php7.0/fpm/pool.d/www.conf

Look for the lines that look like this:

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group will be used.
user = nginx
group = nginx

And replace them with this:

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group will be used.
user = www-data
group = www-data

Once that’s done, we can start the PHP service:

$ sudo service php7.0-fpm start

…and make sure it starts every time we reboot!

$ sudo update-rc.d php7.0-fpm defaults

Step 4: Install MySQL

The engine, the brains, and now the memory center. MySQL is a “relational database” which is a fancy way of saying things are associated with other things. Installation will look familiar at this point:

$ sudo apt-get install mysql-server php7.0-mysql

It’ll ask you to fill in a root password – do so and don’t forget it! We can start this service immediately, as we want to run a few startup commands…

$ sudo service mysql start

…and (you guessed it) start it upon server boot!

$ sudo update-rc.d mysql start

Once that’s done, we can start the initial security lockdown for MySQL. I highly recommend this, as it removes a few unsafe features:

$ sudo /usr/bin/mysql_secure_installation

Enter your root password and use the defaults. Once you’re done, there’ll be no anonymous users, no remote logins, no “test” data, and the “privilege tables” will be refreshed to take the other settings into account.

Next, let’s log into MySQL and set up our initial WordPress database. We’re nearly there – this is the home stretch!

$ mysql -u root -p

Enter your root password. Next, we’re going to create a database and user, associate them with each other, and reload the privilege table.

> CREATE USER 'username'@'localhost' IDENTIFIED BY 'secure_password';
> GRANT ALL ON sitename.* TO 'username'@'localhost';
> exit;

If these look familiar to you – as in you’ve seen WordPress’s install screen before – then you know you have most of what you need to install WordPress.

But, before we do that, we need to associate that IP address with your domain name.

If you don’t have a domain, head over to NameCheap (affiliate link) and pick one up. They value privacy over anything else, and even include WhoIs Guard (their private registration) free for a year!

Once you’ve done that (or logged into your own domain registrar), you’ll want to change the A record (the ‘@’record, and possibly the ‘www’ record) to the IP address you created earlier.  Every domain registrar is different – so you may have to poke around on their forums if you’re unsure.

Set the TTL (Time to Live) to between 5 and 10 minutes – no sense waiting all night, right?

Once that’s done, go pour yourself a celebratory drink, because we’re at the finish line.

Step 5: Set up WordPress

Visit your domain. If you’ve followed all the instructions correctly and didn’t receive any error messages, you should see the WordPress installation screen.  Take your username, sitename, and secure_password variables you defined above and enter them in. For the host, type in ‘localhost’.

And that’s it! WordPress is now running on your very own slice of the web, powered by Amazon’s amazing AWS Elastic Instance.

You’ve set up your own server, added PHP7, MySQL, and Nginx, installed WordPress from the command line, and are now ready to reap the benefits of a server that is truly your own.

If you have any questions, I’ll do my absolute best to answer them below.  If I’ve missed a step, or you’ve got some handy-dandy tips for first-time sysadmins, leave those below as well!