Introduction
After building an application, deploying it to a cloud service is crucial in making it accessible to your users. Amazon offers a service called Amazon Elastic Compute Cloud (EC2), which provides scalable and low-cost virtual servers in the cloud.
With EC2, you can easily customize the various specs of your virtual server, such as operating systems, CPU, memory, storage, and other features. You also control your security and network settings, which ensures that data is only accessible to authorized users. EC2 is an ideal choice for hosting applications of different sizes and complexities.
In this guide, you will learn how to deploy a full-stack application that comprises a Typescript Next.js client and Node.js server to Amazon EC2. You will cover steps to:
- Set up an AWS EC2 instance.
- Install PostgreSQL and create a database.
- Deploy your application code onto the instance.
Additionally, this guide will cover how to configure your network and security settings for optimal performance and safety. Let’s get started.
Prerequisites
To complete this guide, make sure you have the following requirements:
- An AWS Account.
- Git bash (Standalone Installer). If you need help installing Git Bash, check out this tutorial — How to install Git on Windows 10.
- PostgreSQL installed. Follow this tutorial — How to Install PostgreSQL 15 on Windows Postgres for guidance on how to install PostgreSQL.
- Some familiarity with PostgreSQL and basic Linux commands. However, if you don't have any prior experience, don't worry. This guide is beginner-friendly. But, If you are interested in learning more about these topics, you can acquire the necessary knowledge through the following courses:
Step 1: Launch an EC2 instance.
In this step, you will set up and launch an AWS EC2 Ubuntu instance.
The Ubuntu operating system is a popular choice when setting up an AWS EC2 instance. It is open-source and comes with no licensing fees. Ubuntu supports a vast range of software and has a thriving community that makes it easier to find help.
To start, log in to your AWS console. Next, search and select EC2 using the search bar.
Click on Launch instance to begin set up and launch your AWS EC2 instance.
To proceed, provide a unique and descriptive name for your instance.
Next, in the Application and OS Images section, select Ubuntu.
By default, the Instance type section is preselected for you. The default option is sufficient for this guide and will save you money. But if you want more resources and power, you can select a higher instance type, which will come at a higher cost.
In the Key pair section, click Create new key pair to generate a new key pair. This key pair will be used to securely connect to your AWS EC2 Ubuntu instance using SSH.
You will next be prompted to set up your Key pair. Enter a Key pair name, keep the default options, and click on Create key pair.
Once you click Create key pair, your Key pair will be downloaded automatically.
In the Network Settings section, leave the first option as default. Beside the SSH checkbox, click the dropdown menu to select the location from which you want to connect to your instance.
There are three options available in the dropdown menu:
- The 0.0.0.0/0 option allows you to connect to your instance from any machine.
- The custom option provides greater control over who and where your instance can accept connections.
- The My IP option allows you to only connect to your instance from your local machine.
Finally, enable the HTTP and HTTPS options to ensure your instance is accessible via the web.
Keep the default options for the Configure storage and Advanced details sections, as they offer the best performance for your EC2 instance. However, if you have specific requirements, you can customize them.
Click the Launch instance button to launch your instance.
You have successfully launched your instance. Next, you will establish a secure SSH connection to your instance.
Step 2. Connect to your AWS EC2 Ubuntu Instance
You will establish a connection to your AWS EC2 Ubuntu instance using SSH. SSH ensures secure and encrypted access to your instance. To authenticate the connection, you will use the key pair you created earlier to verify your identity and access the instance.
Navigate to your EC2 dashboard and click on Instances (running) in the resources tab to access your running instance(s).
After navigating to the Instances page, click on the instance that you created recently.
Next, click on the connect button located in the top-right corner of your screen. This will take you to the Connect to Instance page, where you can connect to your instance via several options.
Select the SSH client tab to review your connection details.
To connect to your instance, open Git Bash, navigate to the directory where your Key pair was downloaded, and execute the following commands:
Press CTRL + C to copy and CTRL + Shift + Insert to paste in Git Bash.
chmod 400 "your-key-pair-name"
ssh -i "your-key-pair-name" ubuntu@ec2-*-*-*.eu-north-1.compute.amazonaws.com
After executing the command, you will get the following prompt:
The authenticity of host 'ec2-*-*-*-*.eu-north-1.compute.amazonaws.com (*.*.*.*)' can't be established.
ED25519 key fingerprint is SHA256:<some-text>.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
Type yes
and hit the ENTER button to proceed.
If connected, your current directory should change to:
ubuntu@ip-your-ip:~$
You have now successfully connected to your AWS EC2 Ubuntu instance. The next step is to update and upgrade your instance to ensure you have the latest security patches and features.
Step 3: Update and Upgrade your instance.
Here, you will update and upgrade your instance to ensure it is up to date with the latest updates and upgrades.
Run the following commands to update and upgrade your instance:
sudo apt update
sudo apt upgrade
When you run the sudo apt upgrade
command, you may get this message:
After this operation, 36.9 KB of additional disk space will be used. Do you want to continue?[Y/n]
Type Y
and press the ENTER key to continue.
Next, you may get a prompt to upgrade the kernel. Press the ENTER button to proceed with the upgrade. Once you do that, a new prompt will appear where you will check specific values.
To continue, navigate the list of services using the ARROW keys. Use the SPACEBAR key to mark each unchecked checkbox. Once all the checkboxes are marked, press ENTER to complete the process.
Your instance is now ready for use. Next, you will set up PostgreSQL.
Step 4: Setting up a PostgreSQL Database.
In this step, you will install, and set-up PostgreSQL for your application on your EC2 instance. While you could have used other databases and saved time, being able to set up a traditional, relational database like PostgreSQL in a Linux environment is a valuable skill.
It is recommended to create a database for your application in your local environment and then transfer a copy of it to your instance, rather than creating a new database on your instance.
This approach saves time, particularly if you already have a database on your local environment. Additionally, transferring a copy of your local database ensures data accuracy, and consistency as your local database may contain a large amount of data.
Run the commands below in your local environment using the command prompt or any terminal of your choice to:
- connect to postgres
- create a new database, connect to it
- create a table
- exit
psql -U postgres
create database todo_app;
\c todo_app;
create table todo (id BIGSERIAL NOT NULL PRIMARY KEY, newItem VARCHAR(255) NOT NULL);
\q
Next, use the following command to create a backup of the newly created database in your local environment:
pg_dump -U postgres -f todo_app.pgsql -C todo_app
In the command above:
-
pg_dump
is a CLI tool for creating database backups. - The
-U
flag specifies the user to connect to PostgreSQL with. - The
-f
flag specifies the output file, which in this case istodo_app.pgsql
. - The
-C
flag is used to include commands for creating thetodo_app
database in the backup file specified by-f
.
After running the pg_dump
command, the todo_app.pgsql
file will be downloaded in the current directory. Move the backup file to the directory where your AWS Key pair is. Your key pair will be needed to authenticate your connection and accept incoming files.
You will use the SCP protocol to move your backup file to the home directory of your Ubuntu instance. To do this, run the following command:
scp -i your-key-pair-name todo_app.pgsql ubuntu@ec2-*-*-*-*.eu-north-1.compute.amazonaws.com:/home/ubuntu/
If the transfer was successful, you should get this message:
todo_app.pgsql 100% 2372 11.3KB/s 00:00
Now that you have successfully created and transferred a copy of your local database to your instance. Head back to Git Bash to set up PostgreSQL in your instance.
If you get logged out of your session, run the SSH command again to log back in.
ssh -i "your-key-pair-name" ubuntu@ec2-*-*-*.eu-north-1.compute.amazonaws.com
To install PostgreSQL on your instance, use the command:
sudo apt install postgresql postgresql-contrib -y
After installing PostgreSQL, a default user called postgres
is created. You can view the user by running the command:
sudo cat /etc/passwd | grep -i postgres
Output:
postgres:x:115:123:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
To set up PostgreSQL, switch to the postgres
user:
sudo -i -u postgres
If connected, your current directory should change to:
postgres@ip-your-ip:~
Create a new user using the command:
createuser --interactive
You will be prompted for the following information. Enter your answers accordingly:
- Enter name of role to add: ubuntu
- Shall the new role be a superuser? n
- Shall the new role be allowed to create databases? y
- Shall the new role be allowed to create more new roles? n
Exit the postgres
user using the command:
exit
Use the command below to create a password for the ubuntu
user.
psql -d postgres
ALTER USER ubuntu PASSWORD 'password';
\q
If you are curious why you can connect to your database using
psql -d <database>
without specifying a user, this happens because PostgreSQL will try to connect with the same user as your logged-in user, which in this case is ubuntu. If the user ubuntu exists in Postgres, the connection will go through without being prompted for a password, as PostgreSQL uses peer connections for local connections.
Now, create a database and import the contents of the backup database file you created in your local environment:
psql -d postgres
create database todo_app;
\q
psql todo_app < /home/ubuntu/todo_app.pgsql
Output:
.
.
.
ALTER TABLE
To connect to your todo_app
database, execute the command:
psql -d todo_app
select * from todo;
\q
Your PostgreSQL database is ready, you can proceed to set up your application.
Step 4: Set up your application
Now, you will clone the application code from GitHub and get your server and client ready for deployment.
Server set up
To install Node.js run the command:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
You may be prompted by the kernel, press ENTER to proceed.
Clone the application into your Ubuntu instance:
cd ~
git clone https://github.com/backendbro/todo-app-ubuntu.git
Install the dependencies required by the server:
cd todo-app-ubuntu/server
npm install
sudo npm install -g nodemon
When working in a production environment, it's not recommended to run node server.js
directly. This is because the Express server will shut down whenever you exit your instance. To avoid this, you can use PM2. PM2 is a process manager that ensures your Express server stay up and running at all times.
To install PM2 globally, run the command:
sudo npm install pm2 -g
Before starting your server, compile your Typescript server with:
npm run build
Now, start your server using PM2 by running the command:
cd ~
pm2 start /home/ubuntu/todo-app-ubuntu/server/dist/server.js --name todo-app
Next, configure PM2 to automatically restart your Express server in case of a crash or reboot using the command:
pm2 startup
Output:
[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
To complete the setup for PM2, copy and run the startup script.
Ensure that you copy the script from your instance as yours may differ. The command below is only an example to guide you on what to copy and run.
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
Finally, save your current list of running processes:
pm2 save
Client set up
After starting the server, install the necessary dependencies and run the build command to prepare the client.
cd ~
cd todo-app-ubuntu/client
npm install
npm run build
In the next step, you will configure Nginx to serve your client so it can be accessible on the web.
Step 5: Installing and Configuring Nginx
In this step, you will configure Nginx to serve your client side. You will need a domain name to complete this step. Just in case you have no money to spare, check out this video — Free Domain Name — Use Anywhere, No Credit Cards, NO CATCH! for help on how to get a domain name free of cost.
You can find your Public IPv4 address on your instance dashboard to set up your domain.
To get started, execute the command below to install and enable Nginx:
cd ~
sudo apt install nginx -y
sudo systemctl enable nginx
To set up an Nginx server block, navigate to the sites-available folder.
cd /etc/nginx/sites-available
When you run the ls
command to list the files in the current directory, you'll find a server block named default
which is responsible for handling requests that don't match any other server blocks in the sites-available
directory.
To view the default server block, open http://<your-ec2-instance-Public-IPv4-address>
or http://<your-domain-name>
in your web browser.
You can use the default
server block as a template to create your server block. To do this, run the following command:
sudo cp default todo_app
Open the todo_app
server block for configuration:
sudo vi todo_app
To begin configuring the todo_app
server block, press the I
key on your keyboard to enter INSERT mode. This will enable you to type in the required changes. Replace the contents in the first server
block with the following:
server {
listen 80;
listen [::]:80;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html;
server_name your-ec2-public-ipv4-adddress your-dmain-name.com;
location / {
try_files $uri $uri/ =404;
}
location /api/v1/todo {
proxy_pass http://localhost:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Once you have made the changes, press the esc
key to exit INSERT mode. Next, type :wq
and press ENTER to exit the vim terminal.
Make sure to memorize the sequence for inserting values and exiting in vim, as you will need it later.
Now, enable the new server block by running the command:
sudo ln -s /etc/nginx/sites-available/todo_app /etc/nginx/sites-enabled/
sudo systemctl restart nginx
Use the command below to move your client build files to the root
directory specified in the todo_app
server block:
sudo mv /home/ubuntu/todo-app-ubuntu/client/dist/* /var/www/html
Now, you should be able to access your website using http://<your-Public-IPv4-address>
and http://<your-domain-name>
.
But, at the moment you won’t be able to make request to the Express server, because the Express server cannot access its environment variables. In the next step, you will set up your environment variable.
Step 6: Setting up environment variables
In this step, you will configure your environment to allow your Express server to access its PORT number and the PostgreSQL database.
Create a .env
file:
cd ~
sudo vi .env
Copy and paste in the following environment variables into the .env
file
PORT=4000
DB_USER=ubuntu
DB_HOST=localhost
DB_PASSWORD=password
DB_DATABASE=todo_app
DB_PORT=5432
NODE_ENV=production
If you must enter a different value in the
.env
file, ensure there are no space(s) between the variable name, equal sign and your variable value.
Moving on, open the .profile
file
sudo vi /home/ubuntu/.profile
Enter the following command below into the .profile
file to ensure your environment variables is set at all times, even when your instance reboot or crash.
Make sure to enter the command at the bottom of the
.profile
file
set -o allexport; source /home/ubuntu/.env; set +o allexport
Visual illustration:
For this change to take effect, restart your instance:
sudo reboot
You will be logged out from your Ubuntu instance. Re-connect to move on to the next step.
After setting up your environment variables, you will enable a firewall to filter specific connections.
Step 7: Enable firewall
In this step, you will enable the firewall to filter the type of requests that can reach your Ubuntu instance.
To allow SSH, HTTP, and HTTPS requests, run the commands:
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
To apply your changes, use:
sudo ufw enable
After running the sudo ufw enable
command you will receive this message:
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Type y
and press ENTER.
After filtering specific requests, the next step is to enable SSL. This will ensure your website is secure, and visitors can access it without encountering pesky security warnings on their browsers. With SSL, your site will be safe and trustworthy.
Step 8: Enable SSL
In this step, you will generate an SSL certificate with Certbot and configure Nginx to redirect HTTP traffic to HTTPS.
Install Certbot by running the command:
sudo snap install --classic certbot
Enable certbot using:
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Configure Cerbot for Nginx, with the command:
sudo certbot --nginx
When running the certbot configure command, you will be prompted for the following. Here's how you should answer:
- Enter email address ... ? valid-email@email.com
- Please read the Terms of Service at ... ? Y
- Would you be willing, once your first certificate is successfully issued... ? N
- Which names would you like to activate HTTPS for? ... ? 1
Your SSL certificate has been successfully set up. Now, head over to your website to view it.
Conclusion.
By following this guide, you deployed a full-stack Next.js and Node.js application to an AWS EC2 Ubuntu instance. However, It is important to note that in larger applications, you might need to take a different approach where you may have to split your application services into two or more instances.
In this guide, you covered the fundamental steps involved in the process. Firstly, you learned how to set up and configure an AWS instance. Next, you learned how to set up PostgreSQL on your AWS EC2 Ubuntu instance. You created a new Postgres user, and a new database. You also learned how to set up and configure Nginx and PM2 as a reverse proxy and process manager, respectively. In addition, you learned how to create an SSL certificate, which is crucial for secure and encrypted communication between your application and the user's web browser.
Although some details may vary depending on your specific application, this guide provides a solid foundation for deploying an application on AWS EC2. With this knowledge, you can confidently deploy your application on AWS and take advantage of its scalability, reliability, and security. Happy deployment 😊
Top comments (0)