DEV Community

Cover image for Step-by-Step Guide: Deploying a Full-Stack App on AWS EC2.
Ukagha Nzubechukwu
Ukagha Nzubechukwu

Posted on • Updated on

Step-by-Step Guide: Deploying a Full-Stack App on AWS EC2.

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:

  1. An AWS Account.
  2. Git bash (Standalone Installer). If you need help installing Git Bash, check out this tutorial — How to install Git on Windows 10.
  3. PostgreSQL installed. Follow this tutorial — How to Install PostgreSQL 15 on Windows Postgres for guidance on how to install PostgreSQL.
  4. 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.

aws console

Click on Launch instance to begin set up and launch your AWS EC2 instance.

launch instance button

To proceed, provide a unique and descriptive name for your instance.

aws ec2 instance name field

Next, in the Application and OS Images section, select Ubuntu.

aws ec2 application and os images section

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.

aws instance type section

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.

aws key pair section

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.

aws ec2 key pair section prompt

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.

aws network and setting section

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.

aws configure storage and advanced details section screenshot

Click the Launch instance button to launch your instance.

aws instance being launched screenshot

You have successfully launched your instance. Next, you will establish a secure SSH connection to your instance.

successful aws instance launch screenshot

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).

aws ec2 dashboard screenshot

After navigating to the Instances page, click on the instance that you created recently.

aws instances page screenshot

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.

aws ec2 connect to instance page screenshot

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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.

ec2 ubuntu instance kernel upgrade prompt screenshot

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.

items needed to be upgraded in your ec2 instance screenshot

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


Enter fullscreen mode Exit fullscreen mode

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 


Enter fullscreen mode Exit fullscreen mode

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 is todo_app.pgsql.
  • The -C flag is used to include commands for creating the todo_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/


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

To install PostgreSQL on your instance, use the command:



sudo apt install postgresql postgresql-contrib -y


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

If connected, your current directory should change to:
postgres@ip-your-ip:~

Create a new user using the command:



createuser --interactive


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Use the command below to create a password for the ubuntu user.



 psql -d postgres
 ALTER USER ubuntu PASSWORD 'password';
 \q


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Output:
.
.
.
ALTER TABLE

To connect to your todo_app database, execute the command:



psql -d todo_app
select * from todo;
\q


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Install the dependencies required by the server:



cd todo-app-ubuntu/server 
npm install 
sudo npm install -g nodemon


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Before starting your server, compile your Typescript server with:



npm run build


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Next, configure PM2 to automatically restart your Express server in case of a crash or reboot using the command:



pm2 startup


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Finally, save your current list of running processes:



pm2 save


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

To set up an Nginx server block, navigate to the sites-available folder.



cd /etc/nginx/sites-available


Enter fullscreen mode Exit fullscreen mode

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.

nginx default web display

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


Enter fullscreen mode Exit fullscreen mode

Open the todo_app server block for configuration:



sudo vi todo_app


Enter fullscreen mode Exit fullscreen mode

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;
        }

}



Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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 fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Visual illustration:

.profile illustration

For this change to take effect, restart your instance:



sudo reboot


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

To apply your changes, use:



sudo ufw enable


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

Enable certbot using:



sudo ln -s /snap/bin/certbot /usr/bin/certbot


Enter fullscreen mode Exit fullscreen mode

Configure Cerbot for Nginx, with the command:



sudo certbot --nginx


Enter fullscreen mode Exit fullscreen mode

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)