Deploying WordPress on a Private Subnet in AWS EC2 Using a Linux Server
Tags: devops, aws, cloud, tutorial
Published: 2024-06-06 07:21:22

After conquering the AWS Cloud Resume Challenge, I decided to build another project that would broaden my cloud skills. This time I wanted to deploy the WordPress application using a secure LAMP stack on a private subnet.
The LAMP stack consists of Linux, Apache, MySQL, and PHP and provides an efficient server environment for application development and web hosting.
Here, I will take you through the process of setting up a LAMP stack and deploying WordPress on a private subnet to host secure websites.

The first thing you should do is create a VPC with private and public subnets. Let's see how it's done.

![Creating an VPC](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i28qia2twx0e2dvmvdhb.png)

## Creating a VPC:
Log in to your AWS console and select Create VPC on Your VPC section. There are 2 ways you can go. You can select the VPC section and configure the rest later or select VPC and More and configure it all then and there. I decided to configure everything by myself, so I selected the first option. If you want to skip some steps, select the more options. Anyways, when asked to specify an IPv4 CIDR block for your VPC, put some publicly available IPv4 CIDRs. I put 10.0.0.0/16 which has a range of 65,536 IP addresses from 10.0.0.0 to 10.0.255.255. After creating this VPC, we should start creating the public and private subnets that we will be using for this project.

## Creating our required subnets:
For my purpose, I needed 2 public subnets and 4 private subnets in 2 Availability Zones. To create it, go to the subnet section of your VPC and select Create Subnet. Select the VPC ID of the VPC you just created and fill in the details of the first subnet. Click Add New Subnets for as many subnets as you need and fill them with your desired details.
For my project, I filled them as below:

Subnet name - public-subnet-1
Availability Zone - us-east-1
CIDR block for the subnet: 10.0.0.0/24

Subnet name - private-subnet-1
Availability Zone - us-east-1
CIDR block for the subnet: 10.0.1.0/24

Subnet name - public-subnet-2
Availability Zone - us-east-1
CIDR block for the subnet: 10.0.2.0/24

Subnet name - private-subnet-2
Availability Zone - us-east-1
CIDR block for the subnet: 10.0.3.0/24

Subnet name - public-subnet-3
Availability Zone - us-east-1
CIDR block for the subnet: 10.0.4.0/24

Subnet name - private-subnet-3
Availability Zone - us-east-1
CIDR block for the subnet: 10.0.5.0/24

After creating the subnet, we need to give them a connection outside of the VPC using gateways.

## Creating Internet and NAT gateways:
Internet gateway is the primary reason that determines the difference between public and private subnets. A subnet that routes traffic to IGW is a public subnet and one that doesn't automatically become a private subnet. Create an Internet Gateway from your VPC section and attach it to the VPC that you created.

Private subnets need some way to communicate with the outside world. This can be done by NAT gateways. A NAT gateway is placed in a public subnet and acts like a proxy for traffic from private subnets in the VPC to the rest of the internet. Create a NAT gateway by choosing a public subnet as its subnet and Allocate Elastic IP. After creating the gateways, we need to configure their traffic by using the Route table.

## Configure Route Tables:
The main table that is created while we deploy our VPC is the route table that will be used as the default table for subnets that do not have any table associated with them. We should configure them to be private by making this main table target the NAT gateway. This can be done by clicking Edit Routes and then clicking Add Route. Enter 0.0.0.0/0 as the Destination and 
select NAT Gateway as the Target then select the NAT gateway you created before.
We should create a new public route table for the public subnets that target the IGW. Create a new route table and associate it with the VPC that you just deployed.  Clicking Edit routes and then click Add route and configure the table by entering 0.0.0.0/0 as the Destination and 
selecting Internet Gateway as the Target then selecting the NAT gateway you created above. Now navigate back to the route tables list and select the public route table you just created. Select the Subnet Associations tab click on Edit subnet associations and pick public-subnet-1 and public-subnet-2 subnets you created and save them to make them public subnets.

Doing this will ensure that you have an AWS VPC (Virtual Private Cloud) with public and private subnets with NAT gateway access for private subnets.
For more details refer to [this document.](https://docs.aws.amazon.com/vpc/latest/userguide/create-vpc.html)

Now that the networking part of the project is over, let's create the instance that will host and deploy our WordPress dependencies.

## Creating an Amazon Linux 2023 Instance:
Almost all of the tutorials and guides for setting up LAMP stack out there are based on Amazon Linux 2 AMI or Ubuntu AMI, so to keep things fresh let's deploy the LAMP stack on an Amazon Linux 2023 Image EC2 instance.

Navigate to the EC2 dashboard and click on “Launch Instance”. Choose the most recent “Amazon Linux 2023 AMI”. Choose the t2.micro instance that qualifies for the AWS free tier. You won't be using key pairs to SSH into that instance but if you wish you can Use an Existing Key Pair or Create a New Key Pair. Click the edit button next to network settings and enter the following details to deploy the instance in our private subnet.
VPC - (Choose the VPC you deployed)
Subnet - (Choose one of the private subnets eg: public_subnet_1)
Create a new security group with the following configuration:
SSH: Port 22 Protocol: TCP (Source: Anywhere)
HTTP: Port 80 Protocol: TCP (Source: Anywhere).
HTTPS: Port 443 Protocol: TCP (Source: Anywhere).
Click on “Launch Instance” once everything’s set.

Now since the instance is completely locked down in the private subnet, you cannot access it using normal ways. If you require additional access via SSH or Remote Desktop, you can try one of the following ways.
1) Use a Bastion host in a public subnet that can SSH into our private secure instance in the private subnet. (Old way of doing things, incurs costs due to the provision of EC2 resources)
2) Connect to the private EC2 Instances using SSM.
3) Connect using EC2 Instance Connect Endpoint.

There are loads of resources available for the first method. You can use this [link](https://towardsdatascience.com/going-bastion-less-accessing-private-ec2-instance-with-session-manager-c958cbf8489f) for a detailed tutorial on using the second method. This article will guide you through the third method.

In the instance that you created, click on the connect button at the top. Select EC2 instance connect and click on Connect using EC2 Instance Connect Endpoint. The EC2 Instance Connect Endpoint sublist will be empty so we need to create one. Click on Create New Endpoint and you will be redirected to the VPC section. Select Endpoint and click on Create New Endpoint. Name the Endpoint and choose EC2 Instance Connect Endpoint as the Service Category. Select your VPC and create a new SG with no inbound rules and an Outbound rule allowing all traffic. Choose this SG for your endpoint. Select the same subnet that your instance is running on (eg: private_subnet_1). Now create the endpoint and choose it as the endpoint for EC2 Instance Connect Endpoint. Now this will allow you to SSH into your instance on the private server.
Now that we can access our instance, we can SSH into it and set up and deploy our WordPress package. Let's leave the EC2 Instance Connect page alone for now.

Before starting this let us create the RDS database that will store all our data.

## Creating RDS Database:
Instead of creating a host database, we are creating a new RDS database in another subnet and connecting it to our instance. Create a Database with the following configurations:
Engine: MySQL
Template: Free Trial
DB Instance Identifier: wp_database
Master Username: admin
Autogenerate or type in your password (remember this if you typed it in)
DB Instance Class: db.t3.micro
Connectivity: Don't Connect to an EC2 Compute Resource
VPC: (Choose your VPC)
Subnet Group: Default
Public Access: No
Choose the default option for rest and create the database.

After creating the database, edit the inbound rules for the SG security group by adding a rule of type MYSQL/Aurora and Source being Custom and the ID of your instance security group(This step is very important). 
<u>Now write down the Username, Password and get the DB endpoint address</u> (It will usually be like wp-database.msifunffjxhn.us-east-1.rds.amazonaws.com)
Now this will ensure that your instance can access the RDS database.

Now let's SSH into the instance using EC2 Instance Connect Endpoint and set up and deploy our LAMP stack and WordPress application.

## Setting up LAMP Stack:
Type the following codes in the instance command shell to install and run the LAMP stack. 

`sudo dnf update -y
`
`sudo dnf install -y httpd wget php-fpm php-mysqli php-json php php-devel
`
`sudo dnf install mariadb105-server
`
`sudo systemctl start httpd
`
`sudo systemctl enable httpd
`
`sudo systemctl enable mariadb
sudo systemctl start mariadb
`
To connect our RDS database with Wordpress,
`
mysql -h wp_database.xxxxx.us-east-1.rds.amazonaws.com -u admin -p`
(Note: do not copy-paste this, ~~wp_database.xxxxx.us-east-1.rds.amazonaws.com~~. Get your endpoint address from your RDS Database)
Enter Password: (Note: The password you type will be blank.) (This password is the one you configured while creating RDS database).

## Deploying WordPress:

`cd /tmp
sudo wget https://wordpress.org/latest.tar.gz
sudo tar xzvf latest.tar.gz
`
`cd Wordpress
sudo mv * /var/www/html/
cd /var/www/html/
sudo mv wp-config-sample.php wp-config.php
sudo nano wp-config.php`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w7ccbx9cnmg80u8ob22d.jpg)

Replace database details with the name, user, and password you just created. Replace localhost with your RDS database endpoint (wp_database.xxxxx.us-east-1.rds.amazonaws.com).

Now your instance will have all the necessary dependencies to host WordPress. But you have no way of accessing it since it is in the private subnet. To access it, you need to use an Application Load Balancer to route all the HTTP traffic to the instance.

## Deploying Load Balancer:
Create a security group for the Load balancer. Edit Inbound rules to HTTP/80/Anywhere. 
Create Target groups so that the load balancer routes the traffic to the registered targets in the Target group. Provide the target group name, Protocol, Port, VPC, and Health checks. Register the two Private EC2 instances in the Target group
It is now time to create the Application Load Balancer
Give a name to the ALB. Select the VPC you created and click on one of the public Subnets in ALB (only Public Subnets will receive HTTP requests).Add the security group you created. Choose the Target group created above under Listeners and Routing. Wait for 5 mins.
Copy the DNS name from Load Balancer and paste the DNS address in a browser.

![Final Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0hz3vn7i3923ncjw5weu.png)

You should be able to see the WordPress Configuration page.
Enjoy.