This is a summary of (many) tutorials on deploying a Node-served webpage on your own domain. Each step contains a reference to the source material (hostinger, digitalocean, nginx and certbot tutorials)
Although every article summarized was written by specialized professionals, I am not a senior specialized professional. Do not take this summary steps as enough to fully secure your application.
Web security, Sysadmin, Infra and Devops are careers theirselves.I do not recommend configuring a server from scratch without support from senior specialized professionals.
I only did it for the sake of learning and ecouraging others to learn more about deploy.
To deploy a page on your own website, you'll need:
www.yourdomain.com
I used hostinger for both buying a domain and renting a hosting service (in my case, a VPS).
If your website is static (Pure HTML/CSS/Javascript) you can use a
simpler hosting solution
that allows uploading assets and setting a index.html
.
All server configuration is automated through an online GUI.
That's not the case for you Node app, that exposes a port and defines routes. Some hosting platforms provide automatized solutions for that. For sake of learning more about deploying, I've chosen VPS, - Virtual Private Server, whichs requires further configuration.
DNS propagation can take up to 24h, which is the reason we're setting DNS first.
If you're using hostinger it's here
At hpanel.hostinger.com/domain/your_domain.com/dns, delete:
A
with names exactly @
or www
. AAAA
with names exactly @
or www
.CNAME
with names exactly @
or www
.Don't delete any other records.
A
name @
pointing to your VPS IPV4A
name www
pointing to your VPS IPV4AAAA
name @
pointing to your VPS IPV6AAAA
name www
pointing to your VPS IPV6Check it at https://www.whatsmydns.net/
We'll setup SSH keys as soon as possible. But before that we'll update packages and create an user.
On linux, the ssh connection syntax is:
ssh <username>@<ip> -p <port>
Use root
as there's no other user yet. Standard SSH port
is 22.
ssh root@<ip> -p 22
apt update
apt upgrade
It's a good practice to avoid operating as root when not necessary.
adduser username
Select a password and press ENTER for the user info settings.
usermod -aG sudo username
su - username
# Run on the COMPUTER YOU'LL USE TO CONNECT TO VPS, NOT THE VPS ITSELF
cd ~/.ssh/
ssh-keygen -t rsa
Select a filename for saving your ssh key. I'll use sitessh
.
Enter a passphrase.
This will generate a pair of public/private ssh key, sitessh
(private)
and sitessh.pub
(public), both at /home/yourUser/.ssh
. Do not share your
private ssh key.
Do
cat siteuser.pub
And copy the output to your clipboard.
Login again at your vps at your newly created user.
NOTE: ~/
is a shortcut to your /home/currentUser
folder. This process will
enable ssh key login to the current terminal user. If you need multiple users
you'll have to repeat the process multiple times.
.ssh
folder at ~/
authorized_keys
file at ~/.ssh
# Run on your VPS at you new user account
mkdir ~/.ssh
chmod 700 ~/.ssh
vim ~/.ssh/authorized_keys
After opening the file:
i
to enable write modeshift insert
to paste your pub ssh key from clipboardesc
to exit write mode, then :wq
to exit saving changes.authorized_keys
chmod 600 ~/.ssh/authorized_keys
Exit user with
exit
At your client computer, try to ssh connect to your user:
ssh -p 22 <user>@<ip>
After login into VPS, open SSH config file:
sudo vim /etc/ssh/sshd_config
There will be a key/value pair:
#PasswordAuthentication yes
Delete the comment with shift backspace
and set the key to no:
PasswordAuthentication no
Reload ssh deamon
sudo systemctl reload ssh
This will disable user/password login to all users, including root
. Notice your
SSH key was set only for your new user
. Therefore you'll not be able to login
into root
any longer.
If for some reason will need to access root
user, do sudo su
. If for some
reason you need to enable ssh login to root, follow this tutorial
Iptables is the native program for configuring ipv4 packages filtering. UFW (Uncomplicated Firewall) is an interface for setting up iptables more smoothly.
Usually UFW comes pre-installed but disabled in VPSs by default. If that's not the case, install with:
sudo apt install ufw
The approach is to deny all conections by default and create a restricte list with the allowed connections.
sudo ufw default deny incoming
sudo ufw default allow outgoing
By standard ssh connections use tcp protocol over port 22.
You can allow this specific connection
sudo ufw allow 22/tcp
Or allow the alias
sudo ufw allow ssh
Both approaches have the same effect.
sudo ufw status numbered
As I just configured SSH connections, this is my table:
Status: active
To Action From
-- ------ ----
[ 1] 22/tcp ALLOW IN Anywhere
[ 2] 22/tcp (v6) ALLOW IN Anywhere (v6)
We will enable HTTP/HTTPs later when configuring NGINX. We will use git for transfering files. If that's not your case, enable sftp.
After setting all your desired rules (be sure to allow ssh otherwise you will get locked) enable the firewall:
sudo ufw enable
(Additional): Drop a rule
Notice the numerical column on the table. You can drop a rule by:
# Not a step
sudo ufw delete <ruleNumber>
(Additional): Reset Firewall
# Not a step
sudo ufw reset
(Additional): Disable UFW
# Not a step
sudo ufw disable
I'll use Node 16
cd /tmp
suo apt install curl -y
curl -sLO https://deb.nodesource.com/setup_16.x
sudo bash setup_16.x
sudo apt install -y nodejs gcc g++ make
node -v #Check if installation was sucessful
npm -v #Check if NPM was installed
Reverse proxy is an intermediate web server that will:
It's a good idea to use nginx for many reasons:
Although modern Node versions can handle many of the tasks above, it's a good idea to use Nginx as a layer that only handles/enchaces requests while keeping Node handling only the necessary backend core operations.
sudo apt update
sudo apt upgrade -y
sudo apt install nginx -y
sudo ufw allow 'Nginx Full'
Open your VPS ipv4
address on browser. You should see Nginx default page.
If that's not the case, your VPS is probably configured to use apache2
as standard server.
Check if Nginx is runnig/your vps is running apache2:
systemctl status nginx
systemctl status apache2
If that's the case, let's unninstall apache and restart Nginx:
sudo service apache2 stop
sudo apt-get purge apache2 apache2-utils apache2-bin apache2.2-common -y
sudo apt-get autoremove
sudo rm -rf /etc/apache2
sudo systemctl enable nginx
sudo systemctl restart nginx
Check again:
systemctl status nginx
systemctl status apache2
Don't worry if you still see apache2 initial page at this point.
It happens because both nginx and apache2 use /var/www/html
as the standard
folder for serving content. Nginx is the one serving apache2 standard HTML.
Server Blocks are used to encapsulate a domain. The default server block is
configured to serve files ate /var/www/html
.
1- Create a server block for your domain:
sudo mkdir -p /var/www/yourdomain.com/html
2- Assign ownership of the directory with the $USER environment variable
sudo chown -R $USER:$USER /var/www/yourdomain.com/html
3- Allow the owner to read, write, and execute the files while granting only
read and execute permissions to groups and others
sudo chmod -R 755 /var/www/yourdomain.com
4- Create a sample index.html page and fill it with a testing content
vim /var/www/yourdomain.com/html/index.html
<html>
Your server block is being served
</html>
5- Create a configuration file for your server block
sudo vim /etc/nginx/sites-available/yourdomain.com
and paste inside it the configuration:
server {
listen 80;
listen [::]:80;
root /var/www/yourdomain.com/html;
index index.html index.htm index.nginx-debian.html;
server_name yourdomain.com www.yourdomain.com;
location / {
try_files $uri $uri/ =404;
}
location /.well-known/acme-challenge {
default_type text/plain;
root /etc/letsencrypt/webroot;
}
}
6- Enable the file by creating a link from it to the sites-enabled directory, which Nginx reads from during startup:
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
7- Uncomment and adjustserver_names_hash_bucket_size
at /etc/nginx/nginx.conf
to 64 (this avoid a possible hash bucket memory problem that can arise from adding additional server names):
sudo vim /etc/nginx/nginx.conf
Also change include /etc/nginx/sites-enabled/*;
to include /etc/nginx/sites-enabled/yourdomain.com;
8- Run nginx test:
sudo nginx -t
9-Restart nginx to apply changes:
sudo systemctl restart nginx
10-Finally, type your VPS ipv4
on browser and check if changes applied. Use ctrl f5
to reload clearing cache.
sudo apt-get remove certbot
Hostinger VPS has issues with snap
. Therefore we will use Python's pip
to
install certbot.
sudo apt update
sudo apt install python3 python3-venv libaugeas0
sudo python3 -m venv /opt/certbot/
sudo /opt/certbot/bin/pip install --upgrade pip
sudo /opt/certbot/bin/pip install certbot certbot-nginx
sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot
This will only work if you already have a nginx instance providing an http
server over port 80
.
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com -v
Finally your (test) website should be acessable at www.yourdomain.com!!!
sudo apt-get update
sudo apt-get install git
git config --global user.name "<your name>"
git config --global user.email "<your email>"
git config --global user.username"<your username>"
cd ~/.ssh/
ssh-keygen -t ed25519 -C "your_git_email@example.com"
Call it gitkey
.
1- Start to your ssh-agent
:
eval "$(ssh-agent -s)"
2- Add the private key to your agent:
ssh-add gitkey
3- If you're facing issues with ssh not working on reboot, add the two commands
above to your ~./bashrc
file.
vim ~/.bashrc
# Paste both commands above at the bottom of your ~/.bashrc
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/gitkey
4- Copy the public key content:
cat gitkey.pub
5- Paste it at Github SSH key manager as an Authentication Key
cd ~
git clone git@github.com:<user>/<repo-path>.git
Also enter your project and install your dependencies:
cd <your_repo>
npm install
PM2 allows running the node application as a background process and facilitates integration with nginx proxy.
1- Install PM2
sudo npm install pm2@latest -g
2- Run your node app through PM2
pm2 start <your_script>.js
3- Set PM2 to run every system startup:
pm2 startup systemd
This will output a command such as
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u <user> --hp /home/<user>
Execute it.
4- Save the PM2 process list and corresponding environments in an instance:
pm2 save
5- Set PM2 to run your instance every startup: reboot the system for pm2 to properly apply its changes:
sudo reboot
then
sudo systemctl start pm2-<user>
1- Open up your server block configuration file for edition:
sudo vim /etc/nginx/sites-available/yourdomain.com
2- Replace your /location
block for:
server {
...
location / {
proxy_pass http://localhost:<port your Node app is exposing>;
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;
}
...
}
3- Check for syntax errors on your serverblock conf file:
sudo nginx -t
4- Restart Nginx
sudo systemctl restart nginx
Your website is now fully running at your domain.com!!
Until implementing a proper CI/CD, you can update you website by:
git pull
pm2 restart server
sudo systemctl restart nginx