Building a scalable web server on AWS

In this post, we are going to talk about AWS. There are many AWS examples to create a simple web server with EC2. But a simple web server with a database in the same EC2 instance is not very scalable. Since web server and a database server is in the same virtual machine, it is hard to add another web server to scale out. In this post, I am going to walk through how I created an infrastructure for a web application (moodle) I installed in AWS.

Virtual Private Cloud(VPC)

Many of AWS resources depends on VPC. So let’s start with VPC.

If you use your computer at your home or office, the chances are that your IP address starts with 192.168.0.* or, which is different from your public IP address. That is because you are within your private network. Since your computer is in private network, outside world cannot initiate a connection to your machine. Try to create a web server on your laptop; you will be able to reach your web server within your home or office. But once you leave outside of your place, you won’t be able to reach your web server unless you make some unique configuration to your router.

AWS VPC is like your home or office network behind the router (or firewall). The AWS resources defined within the VPC does not have visibility to outside world unless you define a rule for outside exposure. And you have total control of what IP address ranges for these resources. And you can set inbound traffic rules to any of these resources. In this example, we want to provision database server and file server that are accessible from a web server, but not from outside of this private network. So it makes perfect sense to provision these resources within the VPC.

However, having a private network in the cloud that does not accept any inbound traffic would not be very useful. You can’t ssh into it; you can’t serve web pages from it; it sure is very secure, but not useful. So we are going to create a VPC with private and public subnets, where the resources in the private subnets are only available to other resources within the VPC and the ones in the public subnets are open to the internet.

Creating VPC

First, you will have to log in to AWS console. For the security reason, it is recommended to create an IAM user with admin privilege and log in with your IAM user. But that is out of the scope in this article so I will discuss this topic in another post.

Click VPC menu under “Networking & Content Delivery” category.

Before we build VPC, we will need a public IP address that can be attached to the VPC.

Click Elastic IP from the left side panel. And click blue Allocate new address button. And select “VPC” for the scope and click Allocate. We are going to use this address later when we create a  VPC.

Now go back to VPC dashboard and click the big blue “Start VPC Wizard” button.

Open “VPC with public and private Subnets” tab on the left and click “Select” button. Name your VPC and rename public and private subnet name to suit your need. Allocate Elastic IP you just created and click “Create VPC” button.

Congratulations! Your VPC is successfully created!

Security Group

A security group is a firewall policy that you can apply to instances in your AWS environment.  With the security group, you can allow inbound and outbound traffics from/to the AWS instances. Since we are going to have a web server, which accepts traffics from the public facing internet, and DB server and file server that accepts traffic from the web server, we need to create two security groups. And let’s call them public security group and private security group.

Default Security Group

Click Security Group on the left side navigation menu. You will find that there is already one security group created. This default security group is created when you created a VPC in the previous step. Select the default security group and open Inbound Rules tab at the bottom. You can see that this security group allows all inbound traffic from the instances with the same security group. I personally like to leave this default security group untouched and create my own security group for my own purpose. So let’s create some.

Public Security Group

Click big blue Create Security Group button on the top and fill the name and description field in the modal window. And select the VPC you just created in the previous section from the drop down menu. Click Yes, Create button to create a security group.

Once it is created, select the security group and open Inbound Rules from the bottom pane. Click Edit button and add the following rules.

Type Source

This rule allows incoming traffic from any device out there with HTTP, HTTPS or SSH protocol.

Click Save button and you will have public security group.

Private Security Group

Click Create Security Group button again to create another security group. Once the modal window opens, fill in the name and description field and select the VPC you just created. Click Yes, Create button.

Just like the previous step, open Inbound Rules from the bottom pane. Click Edit and add the following rules.

Type Source
NFS(2049) Public Security Group
MySQL/Aurora(3306) Public Security Group

We would like DB and file servers only accessible from the web server, which belongs to Public Security Group. So we set the inbound rules that accept NFS and MySQL services from instances with Public Security Group.

If you want to be more strict, you can create two security groups with one accepts inbound traffic for NFS and another for MySQL. But for this simple setup, I will just keep it to one group.


When we created a VPC in the previous section, we’ve created two subnets: one for public and another for private. But for some AWS services, such as EFS, requires two subnets from different availability zones(AZ). Different availability zones mean different data center, which has separate power, water supply, and network. Having multiple instances across multiple availability zones will protect the service from unexpected failure of any of the datacenter.

So let’s create another private subnet.

Click subnet from the left side navigation menu. And you will see two subnets from the list: Public and Private. Since we are going to create another private subnet, let’s rename current private subnet to ‘Private Subnet 1’, so we can create a new one to ‘Private Subnet 2’. Note the availability zone of the private subnet from the bottom pane.

Now click big blue Create Subnet button on top. Once modal windows open, fill in the form with following values:

Name Tag Private 2 You can name anything
VPC Tutorial Chose the VPC you created in the previous step
Availability Zone us-east-1a Chose AZ that is different from another subnet
IPv4 CIDR block Ip addresses on this subnet will starts from

And then click Yes, Create button.  Now you have two private subnets across two different AZ within your VPC.

EC2 Instance

What we’ve done so far is building a room, running wires (VPC) and connecting the wires to a few routers (Subnets.) It is time to create a server and connect it to the system.

For the web server, we are going to create an EC2 instance. Let’s go back to main page of the AWS console. Type EC2 in the search box or click EC2 under Compute section.

Click Launch Instance button on the page. On step1, Choose your favorite OS. In this example, we will use Amazon Linux.

On step2, you can choose the instance type. As you scroll down the menu, it gets more powerful and more expensive. Since we are just getting started and not expecting much traffic or computer usage, let’s choose t2.micro. Click Next: Configure Instance Details button.

On Step 3: Configure Instance Details, fill up the form with following data

Number of instances 1 You can create multiple instances at once, but let’s have only one for now
Purchasing option Spot instance can be cheaper if you can tolerate
Network Tutorial VPC Select the VPC we created earlier
Subnet Public Subnet Select the Public Subnet
Auto-assign Public IP Enable This web server needs a public IP address, so we can connect.
IAM role None Let’s not worry about it now.
Shutdown behavior Stop If you terminate, you will lose all the data written in the system.
Enable termination protection check Protect us from accidental termination and lose of data.
Monitoring I turned it off because of additional charge. We will need it in the future, but not for the tutorial.
Tenancy Shared Lower cost.

Once you fill up the form, click Next: Add Storage.

On Step4: Add Storage, you can add storage devices to your server. By default, you will have 8GB of General Purpose SSD with the selected OS installed,  but you can add additional disk. Let’s have one more disk. Click Add New Volume. As you see on the screen, you can get up to 30GB of EBS for free. Since you already have 8GB of root volume, we will add an additional disk of 22GB. I checked the box for encryption for this additional device.  This disk is /dev/sdb. Remember this, as we will need this name later to use the device. Let’s move on to the next step to add tags.

On Step5, you can add tags to the instances and disk volumes you just created. It is always a good idea to name the things you created on AWS. So I add a name tag.

key value
name Tutorial Web Server

Click Nets: Configure Security Group

In the previous section, we have created a couple security groups: one public security group for the web server and private security group for database and file servers. We are going to apply the public security group to this EC2 instance. Click on Select an existing security group radio box and choose the Public security group you’ve created. The web server will accept the HTTP, HTTPS and SSH connection from the public internet.

Click Review and Launch button to review your configuration. You may see the warning message saying the security group is open to the world. That is what we expected and wanted. So let’s move on.

Click Launch button and create a key pair. After you download this .pem file, keep is a safe place. When you ssh to this machine, you will not need to enter a password. Instead, you will need to use this .pem file. That means, if you lose this file, you will be permanently locked out of the machine. And if you let other users on your computer access this file, anyone can login to the instance you just created. So make sure that only you have the read permission to the file by changing file mode to 400.

Click Launch instance button and it will start creating your EC2 instance.

Once the instance is initialized, you can connect to this instance. You can find the connection instruction by right clicking on the instance on the list and click on connect in the menu.

Now you have your virtual machine, where you can install any web applications. Next, let’s create a separate database server.

Database Server

Technically, you can install a database server on the same EC2 instance you’ve just created in the previous step. Then you will have a web server and database server on the same machine. This configuration will work well for development or very small traffic website. But if you think there is a chance that the site traffic will increase in the future, it is better to pay a small price now to make it more future proof: separating database server is one of them. Then you can add a load balancer with increased number of EC2 web servers by cloning existing EC2 instance without worrying about duplicated database.

And you may also want to create another instance of EC2 to install a dedicated database server. However, AWS provide Relational Database Service, where amazon takes care of most of the database administration tasks. Let’s create a database service for our web application to use.

In AWS console screen, click RDS under Database menu.

While we are creating RDS instances, we are going to need subnet groups for the database instances. The subnet group is a collection of subnets that you create in a VPC. To increase the reliability of the database service, you can have AWS create multiple instances of database servers across multiple subnets. So we will have to tell AWS what are the subnets you’d like database servers to reside. These subnets are typically private subnets and span across different availability zones.

To create a subnet group, click Subnet Group from the left side navigation menu and click Create DB Subnet Group button on top. Fill in the name and description field and select the Tutorial VPC for the VPC ID. Under this VPC, we have created three subnets: one public and two privates. We are adding these two private subnets and click Create button at the bottom.

Now we are ready to create a Database instance. Click Instances from the left side navigation menu. Click big blue Launch a DB Instance button. AWS provide multiple databases engines to choose from. In this example, we will go with MySQL.

Choose DB Engine Version that is supported by your web application. Some applications require a certain version of MySQL. But in this example, we are going with the default version. And you can choose the DB Instance Class and storage type and size.

Name your instance on DB Instance Identifier and create a root username and password at Settings section. Click Next Step to configure Advanced Settings.

For Network & Security section, select Tutorial VPC and tutorial DB subnet group we’ve just created. And we don’t want the database server accessible from outside world, select No for Publicly Accessible. And make sure to have Tutorial Private Security Group for VPC Security Group. I just left default values for Database Options and Backup sections. And Click Launch DB instance button.

Now you have a MySQL database server, let’s try to connect to the database. Remember, the database server is in the private subnet, and the security group does allow traffics from instances with the public security group. So we cannot directly connect to the database from your laptop. We will have to ssh into the EC2 instance we’ve created earlier.

First, let’s find the database endpoint URL. Go to the RDS instances list, then you will see the database we’ve just created in the list. Click on the row and you will see the database endpoint URL.

Then, ssh into the EC2 instance. You may need to install MySQL client to connect to the MySQL server.

Woohoo! We now have a web server and a database server connected.

Elastic File System(EFS)

If your application requires a web server and database server, you don’t need to follow this section and next. But many web application requires file storage for users to upload files. S3 is one of the solutions. But in this tutorial, we are going to use Elastic File System. With EFS, you can mount the EFS as network file system and shared among multiple web servers.

To create a Filesystem, click EFS under Storage section and click Create file system big blue button.

In Step 1, select VPC and choose two private subnets for mount targets. And make sure that the security group for the mount targets is the private security group we created earlier. Click Next Step.

In Step 2, add tags.

In Step 3, review and create a file system by clicking Create File System button.

That was simple! Now we have a file system, how we use it? We need to mount the file system to read/write the files. You can find the mount instruction by opening the Amazon EC2 mount instructions link.

First, let’s log into our web server.

Now you need to install nfs-utils to mount NFS on your server.

And create a directory where web application data are stored.

And mount the file system using the DNS name you find in the file system detail.

Now the files you write to /var/app_data will be stored in the EFS and can be shared by any web servers that have access to the file system.

And if you’d like to automatically mount the EFS when the system boots up, you may want to add one line at /etc/fstab file.


Now we have (almost) everything you need to get started with a simple web application. I have downloaded and installed a PHP application in this setting. However, I noticed the significant performance drop because of the usage of NFS for storage. The particular application that I used expected the storage device to be local device and create caches on the storage device. The network drive is much slower than the local device, degrade the performance of the application. One of the solutions for that was to have the application not use file storage as cache storage and use memcached for its cache storage. But first, we need to create a memcached instance to do that.

Click ElastiCache under Database section.

Just like we created subnet groups in MySQL database, we need to create a subnet group for ElastiCache. Click Subnet Groups from the left side navigation menu and click Create Subnet Group button. Fill in Name and Description fields and list of subnets to be added to this group. And click Create button.

Now click Memcached from the left side navigation menu and click Create button.

Select Memcaced radio button on Cluster engine section. Fill in the Name field. I selected smaller Node type to stay within the free tier.

Next, open the Advanced Memcahed settings section.

Select the Subnet group we’ve just created on the Subnet group field. And click our private security group for the security group.

And click Create button.

Now we have created the memcached instance. But there is one problem. We assigned our private security group to this memcahced. Our private security group does not allow memcached (port 11211). So let’s allow port 11211 for our private security group.

Go to VPC> Security Groups. Select Private Security Group. Open Inbound Rules tab and click Edit button. Add TCP port 11211 from the Public security group and click Save.

Now you should have a functioning memcached and you should be able to access it from your web server.

To test the connection to the memcached, let’s again login to the web server.

You will need telnet to connect to memcached node and node’s endpoint. In AWS console, click on the cluster name to see the list of the node. Then you will see the endpoint URL of the node.

Let’s try to connect to memcached.

Now you can run Memcahced command. Type quit to exit to the shell.


We have dealt with many concepts and services in AWS in this post. The settings we’ve created in this post is far from being perfectly architected. However, I hope after this exercise, you would understand many of the concepts and practices in AWS.


Leave a Reply