How to deploy an Angular/Node App on AWS EC2 (Linux 2 AMI) with SSL (CertBot)
5 min readFeb 14, 2022
In this tutorial, We will deploy a project based on Angular and Node.js on Amazon EC2 Linux 2 AMI. We will also install free SSL certificates using Lets Encrypt and Certbot. Furthermore, we will install PHPMyAdmin and configure access to it. So grab yourself a coffee because we are going live!
- Run
sudo yum update
- Run
sudo yum -y install httpd mod_ssl
- Run
sudo rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
- Run
sudo yum install -y certbot
- Run
sudo certbot certonly --standalone -d your_domain.com
. Enter your email address to receive updates regarding your certificate. Entery
for all the prompts. This will generate a free certificate for your Frontend. - Run
sudo certbot certonly --standalone -d backend.your_domain.com
. Enter your email address to receive updates regarding your certificate. Entery
for all the prompts. This will generate a free certificate for your Backend. We are deploying the backend on a subdomain. - Run
sudo systemctl start httpd
- Create a separate directory for the Frontend by running
sudo mkdir -p /var/www/html/your_domain/public_html
- Create a separate directory for the Backend by running
sudo mkdir -p /var/www/html/your_domain_backend/public_html
- Set folder permissions by running
sudo chmod -R 755 /var/www
- Create a subdirectory to add server config by running
sudo mkdir /etc/httpd/sites-available
&sudo mkdir /etc/httpd/sites-enabled
- Create a new virtual host file by running
sudo nano /etc/httpd/sites-available/your_domain.conf
- Paste the following code inside the your_domain.conf file. You can create separate virtual host files for Frontend & Backend, But I like to keep one virtual host file for the entire project.
Listen 8080
<VirtualHost *:8080>
ServerName ec2_public_ip
ServerAlias ec2_public_ip
ServerAdmin contact@your_domain.com
DocumentRoot /var/www/html/phpMyAdmin
<Directory /var/www/html/phpMyAdmin>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
<VirtualHost *:80>
ServerName your_domain.com
ServerAlias www.your_domain.com
ServerAdmin contact@your_domain.com
DocumentRoot /var/www/html/your_domain/public_html/
<Directory /var/www/html/your_domain/public_html>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
Redirect permanent / https://your_domain.com/
</VirtualHost><VirtualHost *:443>
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/your_domain.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/your_domain.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/your_domain.com/fullchain.pem
ServerName your_domain.com
ServerAlias www.your_domain.com
ServerAdmin contact@your_domain.com
DocumentRoot /var/www/html/your_domain/public_html/
<Directory /var/www/html/your_domain/public_html>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
<VirtualHost *:80>
ServerName backend.your_domain.com
ServerAlias backend.your_domain.com
ServerAdmin contact@backend.your_domain.com
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
Redirect permanent / https://backend.your_domain.com/</VirtualHost>
<VirtualHost *:443>
SSLEngine On
SSLCertificateFile /etc/letsencrypt/live/backend.your_domain.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/backend.your_domain.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/backend.your_domain.com/fullchain.pem
ServerAdmin admin@backend.your_domain.com
ServerName backend.your_domain.com
ServerAlias www.backend.your_domain.com
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://127.0.0.1:3000/ retry=1 acquire=3000 timeout=600 Keepalive=On
ProxyPassReverse / http://127.0.0.1:3000/
</VirtualHost>
- Enable new virtual host file
sudo ln -s /etc/httpd/sites-available/your_domain.conf /etc/httpd/sites-enabled/your_domain.conf
- Goto
cd /etc/httpd/conf
and typesudo nano httpd.conf
and at the end of the file addIncludeOptional sites-enabled/*.conf
. Press ctrl+x -> y -> Enter to save - Restart server
sudo systemctl restart httpd
Installing phpMyAdmin
- Run
sudo amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2
- Run
sudo systemctl enable httpd
- Run
sudo yum install php-mbstring php-xml -y
- Run
sudo systemctl restart httpd
- Run
sudo systemctl restart php-fpm
- Run
cd /var/www/html
- Run
sudo wget https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.tar.gz
- Run
sudo mkdir phpMyAdmin
- Run
sudo tar -xvzf phpMyAdmin-latest-all-languages.tar.gz -C phpMyAdmin --strip-components 1
- Run
sudo rm phpMyAdmin-latest-all-languages.tar.gz
- Run
sudo yum install mariadb-server -y
- Run
sudo systemctl start mariadb
- Run
sudo mysql_secure_installation
- When prompted, type a password for the root account
- Type the current root password. By default, the root account does not have a password set. Press Enter
- Type Y to set a password, and type a secure password twice
- Type Y to remove the anonymous user accounts
- Type Y to disable the remote root login
- Type Y to remove the test database
- Type Y to reload the privilege tables and save your changes
- Run
sudo systemctl enable mariadb
Install utilities
- Run
sudo yum install git htop tmux -y
- Run
curl -sL https://rpm.nodesource.com/setup_16.x | sudo bash
- Run
sudo sudo yum install nodejs
Setup Backend
- Goto home directory by running
cd ~
- Clone your Backend project by running cd your_project_name
- Change directory by running
cd your_project_directory
- Copy your contents from the current directory to the public_html directory by running the following command
sudo cp -a /home/ec2-user/your_project_backend_dir/* /var/www/html/your_domain_backend/public_html/
- Run
cd /var/www/html/your_domain_backend/public_html/
- Install dependencies by running
npm i
- Now you need to set up your database and run migrations. For MySQL, use root as username, 127.0.0.1 as host, and the same password you used while installing phpMyAdmin
- Run
tmux
to start a new session - Now start the project by running
npm start
- Press ctrl+b and then d to detach from the current session and keep it running in the background. You can connect to the session by running
tmux attach -t session_id
. Session id should be 0 if you have no other sessions running
Setup Frontend
- Goto home directory by running
cd ~
- Clone your Frontend project by running
git clone your_project.git
- Change directory by running
cd your_project_directory
- Copy your contents from dist directory to the public_html directory by running the following command
sudo cp -a /home/ec2-user/your_project_frontend_dir/dist/name_of_project/* /var/www/html/your_domain/public_html/
- If you have 404 not found errors for routes then probably your .htaccess file is missing. You can create one and paste the following code into it
RewriteEngine on
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html to allow html5 state links
RewriteRule ^ index.html [L]
# Enable Compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE application/x-font
AddOutputFilterByType DEFLATE application/x-font-opentype
AddOutputFilterByType DEFLATE application/x-font-otf
AddOutputFilterByType DEFLATE application/x-font-truetype
AddOutputFilterByType DEFLATE application/x-font-ttf
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE image/x-icon
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
</IfModule>
<IfModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_item_include file .(html?|txt|css|js|php|pl)$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^application/x-javascript.*
mod_gzip_item_exclude mime ^image/.*
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
</IfModule>
# Leverage Browser Caching
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access 1 year"
ExpiresByType image/jpeg "access 1 year"
ExpiresByType image/gif "access 1 year"
ExpiresByType image/png "access 1 year"
ExpiresByType text/css "access 1 month"
ExpiresByType text/html "access 1 month"
ExpiresByType application/pdf "access 1 month"
ExpiresByType text/x-javascript "access 1 month"
ExpiresByType application/x-shockwave-flash "access 1 month"
ExpiresByType image/x-icon "access 1 year"
ExpiresDefault "access 1 month"
</IfModule>
<IfModule mod_headers.c>
<filesmatch "\.(ico|flv|jpg|jpeg|png|gif|css|swf)$">
Header set Cache-Control "max-age=2678400, public"
</filesmatch>
<filesmatch "\.(html|htm)$">
Header set Cache-Control "max-age=7200, private, must-revalidate"
</filesmatch>
<filesmatch "\.(pdf)$">
Header set Cache-Control "max-age=86400, public"
</filesmatch>
<filesmatch "\.(js)$">
Header set Cache-Control "max-age=2678400, private"
</filesmatch>
</IfModule>
It is LIVE!
Now you can access the Frontend at your_domain.com
and phpMyAdmin at ec2_public_ip:8080
. To monitor hardware usage, you can type htop
to view the task manager (sorta task manager). Now pat yourself in the back because you just deployed a project. Happy coding! :)