Deploying Angular SSR and NestJS Application to VPS
recently I published my first website https://interview.community/ and it was a struggle to make it work fully in the server, also I have read ton of comments with people struggling with it too, because deploying Frontend and Backend application on the same server when the Frontend has SSR is a bit confusing, in this article I will show you from A to Z how to deploy Angular SSR application and NestJS together in clean Ubuntu VPS machine using Nginx as reverse proxy, will enable HTTPS for the domains and everything will be explained to you, mostly whatever in this article will also works fine if you have different frontend or backend, you just need to figure out how to do the deployment for your specific framework, even if your backend is in a total different language you can still follow this article for everything else
for the sake of this article I will be using this https://github.com/robertIsaac/nx-ng-nest nx mono repo
- create new VPS and add user and SSH to it
- clone the repo
- install the project dependencies and run the backend and frontend
- install nginx and make the project public
- enable HTTPS for your project
1. Create new VPS and add user and SSH to it
you shouldn’t use the root user given by your VPS for your day to day usage and deployment, it’s too dangerous if your credentials is exposed
so we will start by creating a new user (let’s call it rob) and give it sudo
first log into the server using ssh root@server-ip-address
and enter the root user password
adduser rob # you will be ased to add password use strong password
usermod -aG sudo rob
test the new user using ssh rob@server-ip-address
and enter the password to make sure everything is okay
for easier connection let’s setup SSH for this new user
then in your PC run ssh-keygen
and follow the instruction, then copy the content of ~/.ssh/id_rsa.pub
and in your server mkdir ~/.ssh
if the folder doesn't exist then nano ~/.ssh/authorized_keys
and paste the content of your PC id_rsa.pub
now when you disconnect and connect again, it shouldn’t ask for the password anymore (but it may ask for the ssh key if you generated one)
this is good practice because it give your user non-admin access and to do something sanative it asks for the password
Clone the project
now you need to run ssh-keygen
in the server then run, and copy the content of ~/.ssh/id_rsa.pub
go to https://github.com/settings/keys and add the new SSH key for the server to give the server access to your repos
then it’s better to clone it inside a repos directory so do mkdir ~/repos & cd ~/repos
and start cloning your repo
if you don’t have git in your server install it using sudo apt update & sudo apt install git
install the project dependencies and run the backend and frontend
first let’s install curl using sudo apt install curl
and if you haven’t installed git run first sudo apt update
then let’s install nvm using curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
then let’s install node using nvm install latest
or you can give it any version you want instead of latest
then in your repo install your dependencies using npm i
yarn
or pnpm i
depending in your repo, if you are using anything other than npm you need to run first corepack enable
then build the project using npm run build
to run the project we should use pm2 install it globally using npm i -g pm2
then in the root of your project you should add ecosystem.config.js
with content like
module.exports = {
apps: [
{
name: 'demo-ng',
script: 'dist/ng/server/main.js',
},
{
name: 'demo-nest',
script: 'dist/nest/main.js',
},
],
};
then just run pm2 start ecosystem.config.js
to start it, you can see the log using pm2 log
to fix any problems you may have
now you can to verify they are running using curl http://localhost:3000
and curl http://localhost:4200
install nginx and make the project public
I believe this is the most confusing part of the setup
first let’s install nginx using sudo apt install nginx
then let’s secure our server using ufw
sudo ufw allow 'OpenSSH'
sudo ufw allow 'Nginx FULL'
sudo ufw enable
be very careful because if ssh is not allowed, you will lose your access to the VPS for good, nginx full will give it both 80 and 443 (http and https)
while in the next step we will enable https, but you still need to give nginx http access so it can correctly redirect your users from http to https, otherwise anyone writing your domain without https will be stuck and will think your website is down
now you need to start adding nginx configuration
nano /etc/nginx/sites-available/website-name
and copy this content there
server {
listen 80;
listen [::]:80;
server_name website.com;
include /etc/nginx/mime.types;
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
location /api {
proxy_pass http://localhost:3000;
}
location / {
proxy_pass http://localhost:4000;
}
location ~* \.(jpg|jpeg|png|gif|swf|svg|ico|mp4|eot|ttf|otf|woff|woff2|css|js)$ {
proxy_pass http://localhost:4200;
add_header Cache-Control "max-age=86400, must-revalidate, s-maxage=2592000";
}
}
here is all the magic happen, first thing is to replace server_name with your website domain without http or https, don’t add port 443 this will happen in the next step
location /api {
proxy_pass http://localhost:3000;
}
this part will direct any traffic that start with /api
to your port 3000 which is running using nest (if you are using different port change it here), for this to work correctly your angular application must use https://website.com/api/
as the root for all API calls, using only /api/
will make it work only in the browser but not in the SSR
location / {
proxy_pass http://localhost:4200;
}
this part will redirect anything else to our angular application
location ~* \.(jpg|jpeg|png|gif|swf|svg|ico|mp4|eot|ttf|otf|woff|woff2|css|js)$ {
proxy_pass http://localhost:4200;
add_header Cache-Control "max-age=86400, must-revalidate, s-maxage=2592000";
}
this part say that any of the following file formats (image, videos, fonts … etc, you can extend them if you have other formats) to also be redirected to 4200 but also will enable caching for them
then finally run this last command sudo ln -s /etc/nginx/sites-available/interview.community /etc/nginx/sites-enabled/
to link the configuration to your enabled sites
the last step to be public is that in your domain settings to add A record to your server IP
example for cloudflare
you can check if it’s working by using ping website.com
and see if it gives you the server ip or something else, if it’s something else you need to wait till it refresh
then you should go and see yourself if it’s working or not
enable HTTPS for your project
this is the final step in this article, you should make sure that your application is working before trying it (it might not work fully because your API url in Angular has https://
)
it’s one of the most easist things to do, just run these commands
sudo apt install snapd
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo certbot --nginx
and that’s it, give it view minutes then go to your https://
to your website and check if it’s working :)
bonus step is to add www.website.com to redirect to your website, you need to also add A record in the domain then sudo nano /etc/nginx/sites-available/www.example.com
with this content
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
you will need to run the certbot again to generate ssl cert for it, or you can do it before running it so it generate both www and non-www cert together