Deploy a full-stack app with PostgreSQL on AWS EC2
Published: 2023-03-10 09:19:02


![Cover](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/brkbeseu8k4uquwjhxgz.png)
Hello, devs! In this article, I invite you on an interesting journey into AWS full-stack development. I had a task to create a dev environment for a full-stack application on EC2 which was created by another developer, the stack is the following:

1. Next JS frontend
2. Node JS + Express JS backend
3. PostgreSQL database
4. AWS EC2

As a secondary task I need to establish an access to the dev database from the pgAdmin console.

I decided to write an article as a walk-through and documentation for other devs as well. It was a bit challenging to find and gather all the necessary pieces together. And eventually, AWS EC2 deploy is much much easier to do than a lot of YouTube videos or online tutorials are explaining.

That being said, let's deploy an app from scratch together!

## Step 1 - Create your EC2 instance

Go to AWS console and find EC2 service. Click "Launch instance"

![ec2 dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ejuzxx52nq8bs5axntlu.png)

This tutorial will be about Ubuntu, but you can try another system of your preference:
![instance configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9fa33zkfpoc8y27raelo.png)

Choose the size of your instance, for demo and test env you can pick micro or small, depends on the size of your app.

You also can create a key for a ssh connection, I already have one, so I will skip this part:

![Create ssh key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ogml97ufl3dbkkkw6xzm.png)

Enabling network settings:

![Enabling network settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1v7po5vkiu7s3hghtt58.png)

Waiting...

![pending](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t5rqnmc5538xy5qr69rl.png)

![running](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ofhchv59f585ht4xuk8.png)

Congratulation, we have launched our EC2 instance!

## Step 2 - Security rules

Lots of tutorials love to skip this part, but I need all your attention here, because without these settings your app will not be accessible. Including your database from your pgAdmin.

Open your instance's dashboard and go to the Security tab:

![Instance dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dz7d5xdbla7v8pfb5t1o.png)

Open your security group rules:

![rules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wy5yg4ojmkfwbycvku11.png)

We need to edit inbound rules here:

![inbound rules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5zqworfgrmdfnqxmw9w.png)

Adding rules for database, backend and frontend, if you have different ports, just use your own values:

![new rules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mbd0kriec49rr4m03kdc.png)

This is how the list of rules should look like when you save it:

![saved rules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jwi9sba84srm4pwow33w.png)

And here we are done with our instance configuration, let's proceed with our server configuration.

## Step 3 - Environment configuration

Go back to the instance and click "Connect"

![connect btn](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1wad183g0u57xhn2ca6.png)

Here you have several options how to connect, if you did not have ssh key, just click connect button.

![connect options](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5lhb44rx5jxzyotn32jx.png)

Blank tab will be opened and you will see this window:

![connection window](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aidzk4ek2u1w9svlzwin.png)

To connect with SSH:

![ssh connect](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mpqcku382pf7zl9ijsem.png)

If you have the following error connecting with SSH using your pem key, [this stackoverflow question](https://stackoverflow.com/questions/8193768/unprotected-private-key-file-error-using-ssh-into-amazon-ec2-instance-aws) can help you.

![bug with ssh](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/axgo9jylj61tlv72013y.png)

I will use Hyper terminal configured as git bash (solved most of the problems btw), as you can see the window is almost the same:

![SSH git bash](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w2aw963wiwuqqx60kus5.png)

Now we need to configure our environment:
- update the system

```
sudo apt update
```

- install nvm

```
sudo apt install curl
curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
```

- install node (you can install any version you want, but 16 is the most stable imho)

```
curl -sL https://deb.nodesource.com/setup_16.x | sudo bash -
sudo apt install nodejs
node  -v
```
or you can install node using nvm:

```
nvm install v16
```
- install database

```
sudo apt install postgresql postgresql-contrib
sudo apt update
sudo service postgresql status
sudo service postgresql restart
sudo -u postgres psql
\conninfo
```
Eventually you should see something like this:

![connected to database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hft8fn1fpmjjwizfwirp.png)

- install pm2

```
sudo npm i -g pm2
```

Now we are ready to configure our database to be accessible from our pgAdmin console.

```
sudo -u postgres psql
\password postgres
```
Setup your database password here and `exit`.

Run this command to open configuration file:

```
sudo vim /etc/postgresql/14/main/postgresql.conf
```
Uncomment listen property and set it to listen all:

![set up listen all](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/igecysbq6xk7ilg7kmiw.png)

then enter esc + :wq to write and quit

Next run this command:

```
sudo vim /etc/postgresql/14/main/pg_hba.conf
```

Place following setting at the end of the file:

```
#IPv4 Addresses
host all all 0.0.0.0/0 md5
#IPv6 Addresses
host all all ::0/0 md5
```
then enter esc + :wq to write and quit

Let's check our configuration, run this commands:

```
sudo systemctl restart postgresql
ss -nlt | grep 5432
```
This should give response as running on global 5432, i.e. 0.0.0.0:5432. This means that psql is now enabled for remote access.

![access is enabled](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5plxsros5cskixnf42zd.png)

Next we can try to connect our server to p4Admin.

Use your public address as a host name:

![host name](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zozrz6advxji0yjjipqb.png)


![register server 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1e0kzc4dqcbd193wkqt5.png)

Give it a meaningful name:

![name](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sr7v3qrslvbzkgzzyidg.png)

And setup the connection details:

![connection details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/48bs0m7ioubf4w4p74zi.png)

Congratulations, our database is connected to the console!

![Congratulations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uqjixwvj6yp2a8yckf7m.png)

## Step 4 - Connecting our Git/Bitbucket with SSH

First of all, let's configure our ssh agent

```
sudo apt update && sudo apt install openssh-client
```
Les's add start command eval $(ssh-agent) to ensure the agent is running when you open a terminal.

```
cd ~
vim .bash_profile
```

add the following:

```
SSH_ENV="$HOME/.ssh/agent-environment"

function start_agent {
    echo "Initialising new SSH agent..."
    /usr/bin/ssh-agent | sed 's/^echo/#echo/' > "${SSH_ENV}"
    echo succeeded
    chmod 600 "${SSH_ENV}"
    . "${SSH_ENV}" > /dev/null
    /usr/bin/ssh-add;
}

# Source SSH settings, if applicable

if [ -f "${SSH_ENV}" ]; then
    . "${SSH_ENV}" > /dev/null
    #ps ${SSH_AGENT_PID} doesn't work under cywgin
    ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null || {
        start_agent;
    }
else
    start_agent;
fi
```

Exist and connect again, you should see this new info:

![ssh agent connection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5d6n7fnd0e3ph1ffkx2z.png)

Check the connection by running this command:

```
ps -auxc | grep ssh-agent
```

![connection established](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ucezej0bqjnwe2fqlf1x.png)

For my projects I created the structure:

```
mkdir projects
cd projects 
mkdir .ssh
cd .ssh
```
Here I am going to store my ssh keys.

```
ssh-keygen -t ed25519 -b 4096 -C "{username@emaildomain.com}" -f {ssh-key-name}
```

> When prompted to Enter passphrase, you can either provide a password or leave the password empty. If you input a password, you will be prompted for this password each time SSH is used, such as using Git command that contact Bitbucket Cloud (such as git push, git pull, and git fetch). Providing a password will prevent other users with access to the device from using your keys.

![generating a key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h02qevzqz0momudddc07.png)

Add key to your bitbucket/git:

![adding a key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zagrimb9m5nztf8cibp8.png)

![add identity](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12d5ygl6p6ngghlpn0uf.png)

![1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u9fblf64lksinjrjjbnc.png)

Cloning for the first time the fingerprint will be created and you will be prompted to enter a passphrase.

Cloning existing backend with ssh:

![clone backend with ssg](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fit8p1fk4amsou7fyeja.png)

Cloning existing frontend with ssh:

![Cloning frontend with ssh](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8g4zk69wbrfv5sowq4j.png)

Don't forget to run `npm install` in each folder and set up env variables properly, remember that your backend is no longer on localhost!

![example of env vars](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x6r3ff5f11mwsm76zc22.png)

Done, we have connected our version control and cloned repos.
In case the current user is no longer valid to develop on this account just delete the key and change config and generate a new key.

## Step 5 - Start our project with pm2

Run build command for frontend app first, then head to the backend and frontend folders and run the command:

```
pm2 start npm --name "process name" -- <start script name>
```

Run pm2 save to save the current list of processes.

![pm2 run project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a14nxk3bxi3odixytwul.png)

And let's check now if everything is correct:

Backend is working :)

![EC2 node js deployed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jkldu56kd5ebaiqirzao.png)

Frontend is working too:

![Frontend](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/20m1vbbgr20pz6c82300.png)

If you have a problem when process like "build" stuck, try to check and kill all the processes and run build command again.

If you have a bug when your instance is unreachable - reboot it and try again. Since we are using t2 micro some processes can overload CPU and memory.



![congratulations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a2u5c1pbfzgiilnjfdd8.png)


Congratulations! Now you have your full-stack app up and running on EC2 instance with database connected to the pgAdmin console!