Alternative

Setup Ubuntu Linux ready for Node Apps

Date: 26.02.2021

Author: Patrick Rottländer

This is the documentation about the basics when you setting up a Linux Ubuntu server to run node-js and express-js applications. It starts with creating a user and accessing the server with an ssh key. I install the required software and show how the directory setup should be done.

We have a new server in front of us and are now starting from the beginning with the preparation of the system to operate nodejs and node express applications.

SSH public key authentication for the root user

First we create the ssh keys on the local machine.

PatrickMBNeu:~ patrick$ ssh-keygen -t rsa -b 4096 -C "your_email@domain.com"
Enter file in which to save the key (/home/patrick/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
PatrickMBNeu:~ patrick$ ls -l .ssh/id_*
-rw-------@ 1 patrick  staff  3434  4 Feb 14:58 .ssh/id_rsa
-rw-r--r--  1 patrick  staff   750  4 Feb 14:58 .ssh/id_rsa.pub
PatrickMBNeu:~ patrick$ 

We log in to the server with the root user via ssh and are in the root user's home directory and create the .ssh directory and the authorized_keys file. The we log out from the server and install the public key from the local machine on the server.

PatrickMBNeu:~ patrick$ ssh root@85.134.111.90
root@85.134.111.90's password: 
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
Last login: Fri Feb 19 07:10:43 2021 from 112.11.237.18
root@h2866085:~# ls -al
insgesamt 28
drwx------  3 root root 4096 Feb 19 07:10 .
drwxr-xr-x 23 root root 4096 Feb 19 06:41 ..
-rw-------  1 root root   12 Feb 19 07:10 .bash_history
-rw-r--r--  1 root root 3106 Aug 14  2019 .bashrc
drwx------  2 root root 4096 Feb 19 07:09 .cache
-rw-r--r--  1 root root  148 Aug 13  2020 .profile
-rw-r--r--  1 root root   20 Feb 19 06:37 .screenrc
root@h2866085:~# mkdir .ssh
root@h2866085:~# ls -al
insgesamt 32
drwx------  4 root root 4096 Feb 19 07:20 .
drwxr-xr-x 23 root root 4096 Feb 19 06:41 ..
-rw-------  1 root root   12 Feb 19 07:10 .bash_history
-rw-r--r--  1 root root 3106 Aug 14  2019 .bashrc
drwx------  2 root root 4096 Feb 19 07:09 .cache
-rw-r--r--  1 root root  148 Aug 13  2020 .profile
-rw-r--r--  1 root root   20 Feb 19 06:37 .screenrc
drwxr-xr-x  2 root root 4096 Feb 19 07:20 .ssh
root@h2866085:~# chmod 700 .ssh
root@h2866085:~# ls -al
insgesamt 32
drwx------  4 root root 4096 Feb 19 07:20 .
drwxr-xr-x 23 root root 4096 Feb 19 06:41 ..
-rw-------  1 root root   12 Feb 19 07:10 .bash_history
-rw-r--r--  1 root root 3106 Aug 14  2019 .bashrc
drwx------  2 root root 4096 Feb 19 07:09 .cache
-rw-r--r--  1 root root  148 Aug 13  2020 .profile
-rw-r--r--  1 root root   20 Feb 19 06:37 .screenrc
drwx------  2 root root 4096 Feb 19 07:20 .ssh
root@h2866085:~# cd .ssh
root@h2866085:~/.ssh# touch authorized_keys
root@h2866085:~/.ssh# ls -al
insgesamt 8
drwx------ 2 root root 4096 Feb 19 07:21 .
drwx------ 4 root root 4096 Feb 19 07:20 ..
-rw-r--r-- 1 root root    0 Feb 19 07:21 authorized_keys
root@h2866085:~/.ssh# chmod 600 authorized_keys
root@h2866085:~/.ssh# ls -al
insgesamt 8
drwx------ 2 root root 4096 Feb 19 07:21 .
drwx------ 4 root root 4096 Feb 19 07:20 ..
-rw------- 1 root root    0 Feb 19 07:21 authorized_keys
root@h2866085:~/.ssh# exit
Abgemeldet
Connection to 85.214.161.41 closed.

PatrickMBNeu:~ patrick$
PatrickMBNeu:~ patrick$ cat ~/.ssh/id_rsa.pub | ssh root@85.214.161.41 "cat >> ~/.ssh/authorized_keys"
PatrickMBNeu:~ patrick$ ssh root@85.134.111.90
root@85.214.161.41's password: 
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
Last login: Fri Feb 19 07:18:57 2021 from 185.17.207.18

root@h2866085:~# cat .ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCfV32OLcC90/0CE0nDsSRZwO5XtyRRBWkgKhkd+wlIad09Vi6URGO3XkUhac2bKWe6t9DP3GWSk23ruMj8M6UV9W1Fb7ZfFW3SXhz5+pRB1v0Uy5PDdxLH1foSz0hpbubCQ0AbEWWRNfMqKC6l2tFWrOfl5AXlbmZsHTH1Th9FSoBhqs8ZH33Oovs+lchzbpmObjUNzr0Y/ZWaNjNlAxvFtt8fMHxqEz3tw7ASub2eaVcGSiNioV3GKwlzbho62AF6b+KGbQkH92P5j4+KnDQpY92Ejd55c4kfq7DcG0pXLC2e77Ci/XnpROzllcOlSjmR5fsAIuWMw7dQyePCar2seVx7WBo0/Z/jnvF0exDJprtxPLlCbFRwj1nVMlKpUsqbE8mZs0L5k7Zh2GLkGJQekYR1X7zDthJHPMLeoepKw20onuCoTkquirwYhy4xCndjZ3VYk0033Rgu13ETrCB+eXc7UrbyyJJyTTs77BQZ/deTLZcXARYU96wQoQGzlevYjyWNhn6WEjkoBc2dcIHzV0Fp3enhLhptG6imHGsvAm+1uNkXbg46hYL4WZdJxkGXOoRo+oT/deRNvzMjDgL2SMUOgSzj7U+Krw0bUCY2LpkWp0lNAuT+YsF2O/k/TEVFBfKthrJd9f/PynTR+IFiRHK7jayhBQXIWSsqI9AlJw== p.rottlaender@icloud.com
root@h2866085:~# 

Ubuntu Version check

Another action we take is to check which OS version and kernel version we are dealing with. This provides important information when we install software later. Often we have to download special software releases for the Linux version used. Type any of the following commands to find OS Name and OS Version running on your Linux server.

root@h2866085:~$ cat /etc/os-release

NAME="Ubuntu"
VERSION="18.04.5 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.5 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

root@h2866085:~$ lsb_release -a

No LSB modules are available.
Distributor ID:    Ubuntu
Description:    Ubuntu 18.04.5 LTS
Release:    18.04
Codename:    bionic

root@h2866085:~$

To print the Linux kernel version running on your server type the following command. I am running Linux kernel version 4.15.

root@h2866085:~$ uname -r
4.15.0
root@h2866085:~$

Create a user

Of course, we don't want to log in to the system with the root user all the time. It is therefore advisable to create another user with whom we can then log in and work.

A user can be created in Ubuntu Linux with the command useradd or adduser. I use the adduser command because with this perl script, in addition to creating the user in /etc/passwd and creating a dedicated user group in /etc/group, the home directory in /home is also created for that user and default files are copied from /etc/skel if necessary.

As root run the command adduser mynewuser.

root@h2866085:~$ adduser mynewuser

Benutzer »mynewuser« wird hinzugefügt …
Neue Gruppe »mynewuser« (1001) wird hinzugefügt …
Neuer Benutzer »mynewuser« (1001) mit Gruppe »mynewuser« wird hinzugefügt …
Persönliche Ordner »/home/mynewuser« wird erstellt …
Dateien werden von »/etc/skel« kopiert …
Geben Sie ein neues UNIX-Passwort ein: 
Geben Sie das neue UNIX-Passwort erneut ein: 
passwd: password updated successfully
Changing the user information for mynewuser
Enter the new value, or press ENTER for the default
    Full Name []: Tech User
    Room Number []: 
    Work Phone []: 
    Home Phone []: 
    Other []: This user is only for tech 
Ist diese Information richtig? [J/N] J

root@h2866085:~$

This create the user mynewuser. In /etc/passwd you see that the user has been created with userID (1000) and a groupID (1000). In /etc/group you find the new group mynewuser. When you check the home directory you find the new home directory in /home/mynewuser.

root@h2866085:~$ grep mynewuser /etc/passwd
mynewuser:x:1000:1000::/home/mynewuser:/bin/bash
root@h2866085:~$ cat /etc/group
....
....
mynewuser:x:1000:
...
root@h2866085:~$ ls -l /home
drwxr-xr-x 2 mynewuser mynewuser 4096 Jan 23 05:31 mynewuser
root@h2866085:~$

The last entry in the /etc/passwd specifies the shell of the new user. Here the newly created user has the Bash shell. In general a Unix shell is a command processor running in a command line window. The user types commands and these commands will be executed and cause actions. For more details you can read the wiki article about Unix Shells.

Because there are some different shells available there might be the need to change the user's shell which basically mean that you change the variety of available commands on your command line. To change the shell of a user type usermod --shell </path/toShell> <mynewuser>. To see which shells you can use pls. check the /etc/shells directory.

root@h2866085:~$ ls -l /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
/usr/bin/screen
/bin/tcsh
/usr/bin/tcsh
root@h2866085:~$ usermod --shell /bin/sh mynewuser
root@h2866085:~$ grep mynewuser /etc/passwd
mynewuser:x:1001:1001:Tech User,,,,This user is only for tech:/home/mynewuser:/bin/sh

Sudo configuration

This newly created user can create files in his home directory and access those files. However, it cannot copy files to directories owned by root. To demonstrate this, I log in to the system with the newly generated user. I create a file in the home directory and then I try to copy this file into a directory owned by root. The error message is that I am not authorized. To do this copy you must be root.

A user can still execute commands as root. That's why there is sudo. So when I try to execute the command with sudo I get the message that the user is not in the sudoers-file.

Patricks-MacBook-Pro:~$ ssh mynewuser@85.214.161.41
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage
Last login: Fri Feb  5 08:57:52 2021 from 87.161.105.46
mynewuser@h2866085:~$  touch testfile
mynewuser@h2866085:~$ ls -l
insgesamt 4
-rw-rw-r-- 1 mynewuser mynewuser 24 Jan 23 06:20 testfile
mynewuser@h2866085:~$ cp testfile /etc/nginx/sites-available/testfile
cp: reguläre Datei '/etc/nginx/sites-available/testfile' kann nicht angelegt werden: Keine Berechtigung
mynewuser@h2866085:~$ sudo cp testfile /etc/nginx/sites-available/testfile
[sudo] Passwort für mynewuser: 
mynewuser ist nicht in der sudoers-Datei. Dieser Vorfall wird gemeldet.
mynewuser@h2866085:~$

Sudo allows the root user of the system to give other users on the system (or groups) the ability to run some (or all) commands as root. The sudo configuration is detailed in /etc/sudoers file.

In my sudo configuration file it is already preconfigured that all users in the sudo group can execute all commands as root. Please note the corresponding entry %sudo ALL=(ALL:ALL) ALL in the /etc/sudoers configuration file below.

I want that the the user mynewuser should be able to execute all commands as root. Therefore mynewuser must be added to the sudo group.

To make mynewuser a member of the sudo group I use the command usermod. Therefore I log in with root using the su root command. Then I run usermod with -a and -G option, which basically says to append a user to a Group.

Finally I check with cat /etc/group that the sudo group contain the mynewuser and logoff from the root account using exit.

mynewuser@h2866085:~$ su root
Passwort: 
root@h2866085:/home/mynewuser# 
root@h2866085:/home/mynewuser# cat /etc/sudoers
#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
Defaults    env_reset
Defaults    mail_badpass
Defaults    secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"

# Host alias specification

# User alias specification

# Cmnd alias specification

# User privilege specification
root    ALL=(ALL:ALL) ALL

# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL

# Allow members of group sudo to execute any command
%sudo    ALL=(ALL:ALL) ALL

# See sudoers(5) for more information on "#include" directives:

#includedir /etc/sudoers.d

root@h2866085:/home/mynewuser# usermod -a -G sudo mynewuser
root@h2866085:/home/patrick# cat /etc/group
root:x:0:
daemon:x:1:
bin:x:2:
...
sudo:x:27:mynewuser
....
root@h2866085:/home/patrick# exit
mynewuser@h2866085:~$

Advanced Package Tool on Ubuntu Linux

But before we start with software installations I need to give you some background info about Ubuntu or Debian package management.

Whenever you want to install software on your Ubuntu system, you can use the Advanced Package Tool or APT for short. Users interact with APT using the apt command. APT download the software package from a package source and then install it.

The software package can only be installed if the package source is known to the APT system. Therefore package sources are listed in the file /etc/apt/sources.list or in further files with the extension .list in the directory /etc/apt/sources.list.d.

When you call apt install <package-name> on your console APT first check the package sources listed in your /etc/apt/sources.list file. When the system find the package on one of these package sources APT will install the software from there.

When the software is not available on any package sources listed in /etc/apt/sources.list APT check the package sources listed in the .list files in your /etc/apt/sources.list.d directory.

When APT does not find the package on any package sources the software package cannot be installed until the package source is entered either in /etc/apt/sources.list or in a separate .list file in the /etc/apt/sources.list.d directory.

The entries in .list files regardless of whether they are in /etc/apt/sources.list or /etc/apt/sources.list.d/*.list are structured identically. The structure of the .list files is divided into 4 sections.

Example:
deb    ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic     main restricted universe
<type> <URI>                                       <archive>  <component>

The above listed package source is an ftp server that contain binary packages for the Ubuntu Bionic Distribution (bionic). The Binary Packages include packages that meet the Ubuntu licensing requirements and are supported by the Ubuntu team (main), software packages that the Ubuntu developers support because of their importance, but which are not under Ubuntu licensing (restricted) and a wide range of free software (no licensing restrictions) that is not officially supported by Ubuntu (universe).

Universe software is maintained by the Masters of the Universe (MOTU Developers). If a MOTU Developer want a software to be included in the Ubuntu package sources, this Developer must suggest the package for the Universe. MOTUs also maintain Multiverse software. Multiverse software is also managed by the MOTUs but the difference is that Multiverse software is not free Software. Multiverse software is subject to licensing restrictions. More details can be read in the Ubuntu Package Management Documentation on the Ubuntu Website.

The .list file can be crated manually using an editor like nano or with the following command. Here in this example to create a mongoDB source file.

echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.2.list

In order to check a package source for authenticity, the GPG key of the package source provided by the manufacturer must also be downloaded and added to the Ubuntu APT keyring using the apt-key add command. Here in this example to add the key to verify the mongoDB source.

wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add -

With wget I download the public key from the mongodb.org server and pipe it into the apt-key add command which finally add the key to the keyring.

Alternatively you can use the add-apt-repository utility or script to automate package source entries in your /etc/apt/sources.list file. From Debian 8 on add-apt-repository is part of the Debian Package software-properties-common which must be installed in case you want to use this utility for your package source management.

apt-get update
apt-get install software-properties-common

Using add-apt-repository the package source will be appended to the /etc/apt/sources.listfile. No separate file will be created in /etc/apt/sources.list.d directory. Here is the example how to add the package source for the mongoDB.

sudo add-apt-repository 'deb [arch=amd64] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.2 multiverse'

The utility add-apt-repository can also be used to install software from Personal Package Archives (PPA). PPAs are a special service for MOTU Developers (Masters of the Universe) to provide Ubuntu packages that are built and published with Launchpad. When you use add-apt-repository to install a PPA a new .list file will be created in your /etc/apt/sources.list.d directory.

add-apt-repository ppa:user/ppa-name

When you use add-apt-repository the PPA's key is automatically fetched and added to the keyring. You can read more about PPA packaging on the launchpad help page.

You should not use PPA software in production environments. The reason is that PPA software is often not maintained regularly by the MOTUs on launchpad. In general MOTUs publish their first versions on launchpad and then integrate them into the main Ubuntu Universe or Multiverse sources. So read the documentation and if possible try to install your production packages from main sources. You can see this in the following example when I install nodejs and npm from a main source.

Nodejs and NPM Installation

To run a node application you definitely need the node platform nodejs and the node package manager npm.

Go to the nodejs.org download page to find out the latest version of node to install. I recommend to install the latest LTS version (Long Term Support) which is at the time of writing this document node version 14.15.5 including npm in version 6.14.11.

When you scroll down on the nodejs.org download page you see the install options. I prefer to install nodejs via package manager. Here you select your distribution so I select Debian and Ubuntu based Linux distributions. The nodejs binaries are available on GitHub nodesource repository. I choose Debian and Ubuntu based distributions (deb) manual installation.

Since I did not installed nodejs via PPA before I can skip the first step described here in the documentation. So I start with the import of the package signing key to ensure that apt can verify the new node source.

$ wget --quiet -O - https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add -

The key ID at the time of writing this document is 1655A0AB68576280.

You can check the imported key using the apt-key command.

$ sudo apt-key fingerprint ABF5BD827BD9BF62

The output should be.

pub   rsa4096 2014-06-13 [SC]
      9FD3 B784 BC1C 6FC3 1A8A  0A1C 1655 A0AB 6857 6280
uid        [ unbekannt] NodeSource <gpg@nodesource.com>
sub   rsa4096 2014-06-13 [E]

Then I create the nodesource.list file in my /etc/apt/sources.list.d directory to make the nodejs package source known to apt. I create 2 entries in that file one for the nodejs debian binaries (deb) and one for the nodejs sources (deb-src).

# Replace $VERSION with Node.js Version you want to install: i.e. node_14.x
# $VERSION=node_14.x
# Replace $DISTRO with the output of the command lsb_release -s -c
# $DISTRO=bionic
$ echo "deb https://deb.nodesource.com/$VERSION $DISTRO main" | sudo tee /etc/apt/sources.list.d/nodesource.list
$ echo "deb-src https://deb.nodesource.com/$VERSION $DISTRO main" | sudo tee -a /etc/apt/sources.list.d/nodesource.list

Then I update the source package list and install.

$ sudo apt-get update
$ sudo apt-get install nodejs

Here is my manual installation.

patrick@h2866085:/etc/apt$ ls -l sources.list.d
insgesamt 0
patrick@h2866085:/etc/apt$ apt-key list
/etc/apt/trusted.gpg.d/ubuntu-keyring-2012-archive.gpg
------------------------------------------------------
pub   rsa4096 2012-05-11 [SC]
      790B C727 7767 219C 42C8  6F93 3B4F E6AC C0B2 1F32
uid        [ unbekannt] Ubuntu Archive Automatic Signing Key (2012) <ftpmaster@ubuntu.com>

/etc/apt/trusted.gpg.d/ubuntu-keyring-2012-cdimage.gpg
------------------------------------------------------
pub   rsa4096 2012-05-11 [SC]
      8439 38DF 228D 22F7 B374  2BC0 D94A A3F0 EFE2 1092
uid        [ unbekannt] Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>

/etc/apt/trusted.gpg.d/ubuntu-keyring-2018-archive.gpg
------------------------------------------------------
pub   rsa4096 2018-09-17 [SC]
      F6EC B376 2474 EDA9 D21B  7022 8719 20D1 991B C93C
uid        [ unbekannt] Ubuntu Archive Automatic Signing Key (2018) <ftpmaster@ubuntu.com>

patrick@h2866085:~$ sudo wget --quiet -O - https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add -
[sudo] Passwort für patrick: 
OK
patrick@h2866085:~$ apt-key list
/etc/apt/trusted.gpg
--------------------
pub   rsa4096 2014-06-13 [SC]
      9FD3 B784 BC1C 6FC3 1A8A  0A1C 1655 A0AB 6857 6280
uid        [ unbekannt] NodeSource <gpg@nodesource.com>
sub   rsa4096 2014-06-13 [E]

/etc/apt/trusted.gpg.d/ubuntu-keyring-2012-archive.gpg
------------------------------------------------------
pub   rsa4096 2012-05-11 [SC]
      790B C727 7767 219C 42C8  6F93 3B4F E6AC C0B2 1F32
uid        [ unbekannt] Ubuntu Archive Automatic Signing Key (2012) <ftpmaster@ubuntu.com>

/etc/apt/trusted.gpg.d/ubuntu-keyring-2012-cdimage.gpg
------------------------------------------------------
pub   rsa4096 2012-05-11 [SC]
      8439 38DF 228D 22F7 B374  2BC0 D94A A3F0 EFE2 1092
uid        [ unbekannt] Ubuntu CD Image Automatic Signing Key (2012) <cdimage@ubuntu.com>

/etc/apt/trusted.gpg.d/ubuntu-keyring-2018-archive.gpg
------------------------------------------------------
pub   rsa4096 2018-09-17 [SC]
      F6EC B376 2474 EDA9 D21B  7022 8719 20D1 991B C93C
uid        [ unbekannt] Ubuntu Archive Automatic Signing Key (2018) <ftpmaster@ubuntu.com>

patrick@h2866085:~$ sudo apt-key fingerprint 1655A0AB68576280
pub   rsa4096 2014-06-13 [SC]
      9FD3 B784 BC1C 6FC3 1A8A  0A1C 1655 A0AB 6857 6280
uid        [ unbekannt] NodeSource <gpg@nodesource.com>
sub   rsa4096 2014-06-13 [E]

patrick@h2866085:~$ lsb_release -s -c
bionic
patrick@h2866085:~$ sudo echo "deb https://deb.nodesource.com/node_14.x bionic main" | sudo tee /etc/apt/sources.list.d/nodesource.list
deb https://deb.nodesource.com/node_14.x bionic main
patrick@h2866085:~$ sudo echo "deb-src https://deb.nodesource.com/node_14.x bionic main" | sudo tee -a /etc/apt/sources.list.d/nodesource.list
deb-src https://deb.nodesource.com/node_14.x bionic main
patrick@h2866085:~$ ls -l /etc/apt/sources.list.d
insgesamt 4
-rw-r--r-- 1 root root 110 Feb 20 08:14 nodesource.list
patrick@h2866085:~$ sudo cat /etc/apt/sources.list.d/nodesource.list
deb https://deb.nodesource.com/node_14.x bionic main
deb-src https://deb.nodesource.com/node_14.x bionic main
patrick@h2866085:~$ sudo apt-get update
OK:1 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic InRelease
Holen:2 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-updates InRelease [88,7 kB]
Holen:3 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-security InRelease [88,7 kB]
Holen:4 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic/main Translation-de [454 kB]
Holen:5 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic/restricted Translation-de [2.268 B]
Holen:6 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic/universe Translation-de [2.272 kB]                       
Holen:7 https://deb.nodesource.com/node_14.x bionic InRelease [4.584 B]                                                  
Holen:8 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-updates/universe Sources [446 kB]
Holen:9 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-updates/main amd64 Packages [1.885 kB]
Holen:10 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-updates/universe amd64 Packages [1.718 kB]
Holen:11 https://deb.nodesource.com/node_14.x bionic/main amd64 Packages [764 B]
Holen:12 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-updates/universe Translation-en [363 kB]
Holen:13 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-security/universe Sources [277 kB]
Holen:14 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-security/universe amd64 Packages [1.109 kB]
Holen:15 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-security/universe Translation-en [248 kB]
Es wurden 8.957 kB in 3 s geholt (3.459 kB/s).                    
Paketlisten werden gelesen... Fertig
patrick@h2866085:~$ sudo apt-get install nodejs
patrick@h2866085:~$ node -v
v14.15.5
patrick@h2866085:~$ npm -v
6.14.11
patrick@h2866085:~$ 

Basically we installed npm in a bundle together with nodejs using the apt package manager. But npm itself is also an additional package manager. We now have apt and npm as package managers on our system.

We need npm to have an additional source for software available on npmjs.com. We will for example install PM2 via npm (see next chapter) but but in particular we need npm to add local software packages or dependencies to our node application projects. So when we want to develop a node app based on the expressjs framework we will install express locally in our project directory. But this will be part of a separate tutorial.

When you ask npm to find outdated packages you can run the npm outdated command (using the -g option to show only global packages).

patrick@h2866085:~$ npm outdated -g --depth=0
Package  Current   Wanted  Latest  Location
npm      6.14.11  6.14.11   7.5.4  global

As you see from an npm point of view the latest version of npm is 7.5.4. But npm know that we installed npm together as a bundle with nodejs via apt and in the apt package sources we say that our wanted version is 14.15.5 (see above). This is a bit confusing but basically npm outdated tell us that that there is a higher npm version available (latest) but on our system we are up to date as the current version and the one we defined in our package sources (wanted) are equal.

We can update npm together only together with the nodejs update which must be initiated with apt-update. And here you see the packages are all up to date.

patrick@h2866085:~$ sudo apt update
OK:1 https://deb.nodesource.com/node_14.x bionic InRelease
OK:2 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic InRelease
OK:3 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-updates InRelease
OK:4 ftp://ftp.stratoserver.net/pub/linux/ubuntu bionic-security InRelease
Paketlisten werden gelesen... Fertig
Abhängigkeitsbaum wird aufgebaut.       
Statusinformationen werden eingelesen.... Fertig
Alle Pakete sind aktuell.
patrick@h2866085:~$  

PM2 Installation with NPM

Go to npmjs.com and search for PM2 Process Manager. You find the PM2 package on npmjs.com. Follow the install instructions.

patrick@h2866085:~$ sudo npm install pm2 -g
patrick@h2866085:~$ pm2 -v
                        -------------

__/\\\\\\\\\\\\\____/\\\\____________/\\\\____/\\\\\\\\\_____
 _\/\\\/////////\\\_\/\\\\\\________/\\\\\\__/\\\///////\\\___
  _\/\\\_______\/\\\_\/\\\//\\\____/\\\//\\\_\///______\//\\\__
   _\/\\\\\\\\\\\\\/__\/\\\\///\\\/\\\/_\/\\\___________/\\\/___
    _\/\\\/////////____\/\\\__\///\\\/___\/\\\________/\\\//_____
     _\/\\\_____________\/\\\____\///_____\/\\\_____/\\\//________
      _\/\\\_____________\/\\\_____________\/\\\___/\\\/___________
       _\/\\\_____________\/\\\_____________\/\\\__/\\\\\\\\\\\\\\\_
        _\///______________\///______________\///__\///////////////__

                          Runtime Edition

        PM2 is a Production Process Manager for Node.js applications
                     with a built-in Load Balancer.

                Start and Daemonize any application:
                $ pm2 start app.js

                Load Balance 4 instances of api.js:
                $ pm2 start api.js -i 4

                Monitor in production:
                $ pm2 monitor

                Make pm2 auto-boot at server restart:
                $ pm2 startup

                To go further checkout:
                http://pm2.io/
                        -------------
[PM2] Spawning PM2 daemon with pm2_home=/home/patrick/.pm2
[PM2] PM2 Successfully daemonized
4.5.4
patrick@h2866085:~$ 

Nano Installation with apt

Nano is part of the Ubuntu 18.04 bionic main packages that are provided via the standard ftp package sources from my provider at stratoserver.net. These standard ftp package sources are in my /etc/apt/sources.list file and can be installed with apt.

patrick@h2866085:~$ sudo apt install nano
patrick@h2866085:~$

Nginx Installation with apt

Also Nginx is a package that is available under the standard Ubuntu main sources. Nginx is part of the Ubuntu 18.04 bionic main packages provided via the standard ftp package sources from my provider at stratoserver.net. Nginx can be installed from these sources using the apt command.

patrick@h2866085:~$ sudo apt install nginx
patrick@h2866085:~$

After the installation is complete the Nginx configuration files are in /etc/nginx directory.

patrick@h2866085:~$ ls -l /etc/nginx
insgesamt 64
drwxr-xr-x 2 root root 4096 Jan 10  2020 conf.d
-rw-r--r-- 1 root root 1077 Apr  6  2018 fastcgi.conf
-rw-r--r-- 1 root root 1007 Apr  6  2018 fastcgi_params
-rw-r--r-- 1 root root 2837 Apr  6  2018 koi-utf
-rw-r--r-- 1 root root 2223 Apr  6  2018 koi-win
-rw-r--r-- 1 root root 3957 Apr  6  2018 mime.types
drwxr-xr-x 2 root root 4096 Jan 10  2020 modules-available
drwxr-xr-x 2 root root 4096 Feb 20 12:46 modules-enabled
-rw-r--r-- 1 root root 1482 Apr  6  2018 nginx.conf
-rw-r--r-- 1 root root  180 Apr  6  2018 proxy_params
-rw-r--r-- 1 root root  636 Apr  6  2018 scgi_params
drwxr-xr-x 2 root root 4096 Feb 20 12:46 sites-available
drwxr-xr-x 2 root root 4096 Feb 20 12:46 sites-enabled
drwxr-xr-x 2 root root 4096 Feb 20 12:46 snippets
-rw-r--r-- 1 root root  664 Apr  6  2018 uwsgi_params
-rw-r--r-- 1 root root 3071 Apr  6  2018 win-utf
patrick@h2866085:~$ 

Then I edit the /etc/nginx.conf as follows.

patrick@h2866085:/etc/nginx$ sudo cat nginx.conf
##
# nginx.conf
##

user www-data; 
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
worker_connections 768;
}

http {

##
# Basic Settings
##

sendfile on;
tcp_nopush on;
tcp_nodelay on;
types_hash_max_size 2048;

include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# Timeout Settings
##
keepalive_timeout  30s; 
keepalive_requests 30;
send_timeout       30s;
##
# SSL Settings
##

ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;

##
# Gzip Settings
##

gzip on;
gzip_vary on;
gzip_comp_level 2;
gzip_min_length  1000;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_disable "MSIE [4-6] \."; 
##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

patrick@h2866085:/etc/nginx$

In the directory /etc/nginx/sites-available you find the sever configuration files and in /etc/nginx/sites-enabled you find the sym links to the sever configuration files that are enabled on your nginx server.

patrick@h2866085:/etc/nginx$ ls -l sites-available
insgesamt 4
-rw-r--r-- 1 root root 2416 Apr  6  2018 default
patrick@h2866085:/etc/nginx$ ls -l sites-enabled
insgesamt 0
lrwxrwxrwx 1 root root 34 Feb 20 12:46 default -> /etc/nginx/sites-available/default
patrick@h2866085:/etc/nginx$ 

First we deactivate the default site by removing the default symlink in /etc/nginx/sites-enabled.

patrick@h2866085:/etc/nginx$ cd sites-enabled
patrick@h2866085:/etc/nginx/sites-enabled$ ls -l
insgesamt 0
lrwxrwxrwx 1 root root 34 Feb 20 12:46 default -> /etc/nginx/sites-available/default
patrick@h2866085:/etc/nginx/sites-enabled$ sudo unlink default
[sudo] Passwort für patrick: 
patrick@h2866085:/etc/nginx/sites-enabled$ ls -l
insgesamt 0
patrick@h2866085:/etc/nginx/sites-enabled$ 

I want to use my nginx server as reverse proxy server for node application servers running on the localhost. Therefore I create a new file prod-reverse-proxy in the directory sites-available .

patrick@h2866085:/etc/nginx/sites-enabled$ cd ..
patrick@h2866085:/etc/nginx$ 
patrick@h2866085:/etc/nginx$ cd sites-available
patrick@h2866085:/etc/nginx/sites-available$ ls -l
insgesamt 4
-rw-r--r-- 1 root root 2416 Apr  6  2018 default
patrick@h2866085:/etc/nginx/sites-available$ sudo touch prod-reverse-proxy
patrick@h2866085:/etc/nginx/sites-available$ ls -l
insgesamt 4
-rw-r--r-- 1 root root 2416 Apr  6  2018 default
-rw-r--r-- 1 root root    0 Feb 21 08:31 prod-reverse-proxy
patrick@h2866085:/etc/nginx/sites-available$ 

Then I put the following content in the file prod-reverse-proxy and link this file into /etc/nginx/sites-enabled.

patrick@h2866085:/etc/nginx/sites-available$ sudo nano prod-reverse-proxy
server {
        listen 80;
        listen [::]:80;
        server_name digitaldocblog.com www.digitaldocblog.com;

        access_log /var/log/nginx/prod-reverse-access.log;
        error_log /var/log/nginx/prod-reverse-error.log;

        location / {

    proxy_set_header HOST $host;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_pass http://127.0.0.1:3000;


  }
}
patrick@h2866085:/etc/nginx/sites-available$ sudo ln -s /etc/nginx/sites-available/prod-reverse-proxy /etc/nginx/sites-enabled/prod-reverse-proxy
patrick@h2866085:/etc/nginx/sites-available$ cd ..
patrick@h2866085:/etc/nginx$ ls -l sites-enabled
insgesamt 0
lrwxrwxrwx 1 root root 50 Feb 21 08:37 prod-reverse-proxy -> /etc/nginx/sites-available/prod-reverse-proxy
patrick@h2866085:/etc/nginx$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
patrick@h2866085:/etc/nginx$ 

The production proxy server is running under the domain server names digitaldocblog.com and www.digitaldocblog.com and is listening on localhost port 80. This production proxy server pass all HTTP traffic from port 80 to a server running on localhost port 3000 (127.0.0.1:3000). Nginx configuration test was tested successful after using nginx -t.

The following commands can be used to check, start and stop the nginx server.

sudo systemctl status nginx

sudo systemctl start nginx 

sudo systemctl stop nginx 

sudo systemctl restart nginx

I restart the nginx server and then check the status. The basic configuration is now complete.

patrick@h2866085:/etc/nginx$ sudo systemctl restart nginx
patrick@h2866085:/etc/nginx$ sudo systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
   Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2021-02-21 08:53:31 CET; 9s ago
     Docs: man:nginx(8)
  Process: 29759 ExecStop=/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid (code=exited, status=0/SUCCESS)
  Process: 29761 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
  Process: 29760 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
 Main PID: 29762 (nginx)
    Tasks: 5 (limit: 60)
   CGroup: /system.slice/nginx.service
           ├─29762 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
           ├─29763 nginx: worker process
           ├─29764 nginx: worker process
           ├─29765 nginx: worker process
           └─29766 nginx: worker process

Feb 21 08:53:31 h2866085.stratoserver.net systemd[1]: Starting A high performance web server and a reverse proxy server...
Feb 21 08:53:31 h2866085.stratoserver.net systemd[1]: Started A high performance web server and a reverse proxy server.
patrick@h2866085:/etc/nginx$ 

Letsencrypt SSL Certificate

To run your server with HTTPS you must install a certificate from an official Centification Authority (CA). Letsencrypt is such a CA where you can get free certificates. Letsencrypt recommends the use of certbot for easy creation and management of domain certificates.

On the certbot site you can select the webserver and your operating system. I choose nginx and Ubuntu 18.04 LTS bionic and get to a website with the install instructions. Since I am not interested in installing certbot with snap, I choose alternate installation instructions and get to the website with the install instructions of the operating system packages.

I must Install certbot and the certbot nginx plugin with apt.

$ sudo apt update
$ sudo apt-get install certbot
$ sudo apt-get install python-certbot-nginx

Then I run certbot --``nginx to request the letsencrypt certificate for my domain and domain servers.

patrick@h2866085:~$ sudo certbot --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): p.rottlaender@icloud.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: digitaldocblog.com
2: www.digitaldocblog.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for digitaldocblog.com
http-01 challenge for www.digitaldocblog.com
Waiting for verification...
Cleaning up challenges
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/prod-reverse-proxy
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/prod-reverse-proxy

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/prod-reverse-proxy
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/prod-reverse-proxy

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://digitaldocblog.com and
https://www.digitaldocblog.com

You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=digitaldocblog.com
https://www.ssllabs.com/ssltest/analyze.html?d=www.digitaldocblog.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/digitaldocblog.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/digitaldocblog.com/privkey.pem
   Your cert will expire on 2021-05-24. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

patrick@h2866085:~$ 

Then I check the new directory and the files in /etc/letsencrypt.

patrick@h2866085:/etc/letsencrypt$ sudo ls -l live
insgesamt 4
drwxr-xr-x 2 root root 4096 Feb 23 06:33 digitaldocblog.com
patrick@h2866085:/etc/letsencrypt$ sudo ls -l live/digitaldocblog.com
insgesamt 4
lrwxrwxrwx 1 root root  42 Feb 23 06:33 cert.pem -> ../../archive/digitaldocblog.com/cert1.pem
lrwxrwxrwx 1 root root  43 Feb 23 06:33 chain.pem -> ../../archive/digitaldocblog.com/chain1.pem
lrwxrwxrwx 1 root root  47 Feb 23 06:33 fullchain.pem -> ../../archive/digitaldocblog.com/fullchain1.pem
lrwxrwxrwx 1 root root  45 Feb 23 06:33 privkey.pem -> ../../archive/digitaldocblog.com/privkey1.pem
-rw-r--r-- 1 root root 682 Feb 23 06:33 README
patrick@h2866085:/etc/letsencrypt$ 

I also check the file prod-reverse-proxy in the directory /etc/nginx/sites-available and see that the file has been updated by certbot. There is a new server section defined (first) an this server is now listening to port 443 ssl and the links to the ssl certificates has been added. The original server section (second) has been changed so that all traffic for hosts digitaldocblog.com and www.digitaldocblog.com will be redirected to the https version of the site (return 301 https://$host$request_uri😉 and requests to port 80 will be answered with 404 site not found.

patrick@h2866085:~$ cd /etc/nginx/sites-available
patrick@h2866085:/etc/nginx/sites-available$ ls -l
insgesamt 12
-rw-r--r-- 1 root root 2416 Apr  6  2018 default
-rw-r--r-- 1 root root 1089 Feb 23 06:34 prod-reverse-proxy

patrick@h2866085:/etc/nginx/sites-available$ sudo cat prod-reverse-proxy
server {
server_name digitaldocblog.com www.digitaldocblog.com;

        access_log /var/log/nginx/prod-reverse-access.log;
        error_log /var/log/nginx/prod-reverse-error.log;

        location / {

    proxy_set_header HOST $host;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_pass http://127.0.0.1:3000;


  }
    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/digitaldocblog.com/fullchain.pem; # managed by Certbot
      ssl_certificate_key /etc/letsencrypt/live/digitaldocblog.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {

    if ($host = www.digitaldocblog.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = digitaldocblog.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;
        listen [::]:80;
        server_name digitaldocblog.com www.digitaldocblog.com;
        return 404; # managed by Certbot
}

patrick@h2866085:/etc/nginx/sites-available$ 

To renew all certificates I must run the following command.

$ certbot renew

To renew all certificates automatically I attach the following line to my system crontab.

40 6 * * * root /usr/bin/certbot renew > certrenew_log

The certbot renew command in this example run daily at 6:40 am (in the morning) as root and log the output in the logfile certrenew_log in the home directory of root. The command checks to see if the certificate on the server will expire within the next 30 days, and renews it if so.

Therefore you must edit the /etc/crontab file as follows.

patrick@h2866085:/etc$ sudo nano crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user  command
15 * * * * root cd / && run-parts --report /etc/cron.hourly
28 0 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
9 5 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 0 20 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )

40 6 * * * root /usr/bin/certbot renew > certrenew_log
#
patrick@h2866085:/etc$

Separate Letsencrypt SSL Certificate for another proxy server

I currently have 2 server files in my /etc/nginx/sites-available directory.

patrick@h2866085:/etc/nginx/sites-available$ ls -l
insgesamt 8
-rw-r--r-- 1 root root 2416 Apr  6  2018 default
-rw-r--r-- 1 root root 1089 Feb 23 06:34 prod-reverse-proxy
patrick@h2866085:/etc/nginx/sites-available$ 

The file default is disabled.

The file prod-reverse-proxy is enabled. The server run as reverse proxy server for the hostnames digitaldocblog.com and www.digitaldocblog.com already using SSL (see above).

For my hostname dev.digitaldocblog.com, which is a valid subdomain of domain digitaldocblog.com I create a separate proxy server file dev-reverse-proxy in the directory /etc/nginx/sites-available, link this file in ****/etc/nginx/sites-enabled, test the nginx configuration and restart nginx.

patrick@h2866085:/etc/nginx/sites-available$ sudo touch dev-reverse-proxy
patrick@h2866085:/etc/nginx/sites-available$ ls -al
insgesamt 16
drwxr-xr-x 2 root root 4096 Feb 23 13:42 .
drwxr-xr-x 8 root root 4096 Feb 23 06:34 ..
-rw-r--r-- 1 root root 2416 Apr  6  2018 default
-rw-r--r-- 1 root root    0 Feb 23 13:42 dev-reverse-proxy
-rw-r--r-- 1 root root 1089 Feb 23 06:34 prod-reverse-proxy
patrick@h2866085:/etc/nginx/sites-available$ sudo nano dev-reverse-proxy
server {
    listen 80;
    server_name dev.digitaldocblog.com;

        access_log /var/log/nginx/dev-reverse-access.log;
        error_log /var/log/nginx/dev-reverse-error.log;

        location / {

    proxy_set_header HOST $host;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

                proxy_pass http://127.0.0.1:3030;
  }

}
patrick@h2866085:/etc/nginx/sites-available$ cd ..
patrick@h2866085:/etc/nginx$ sudo ln -s /etc/nginx/sites-available/dev-reverse-proxy /etc/nginx/sites-enabled
patrick@h2866085:/etc/nginx$ ls -l sites-enabled
insgesamt 0
lrwxrwxrwx 1 root root 44 Feb 23 13:51 dev-reverse-proxy -> /etc/nginx/sites-available/dev-reverse-proxy
lrwxrwxrwx 1 root root 45 Feb 22 19:33 prod-reverse-proxy -> /etc/nginx/sites-available/prod-reverse-proxy
patrick@h2866085:/etc/nginx$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
patrick@h2866085:/etc/nginx$ sudo systemctl restart nginx
patrick@h2866085:/etc/nginx$

Then I run certbot --nginx to request a seperate letsencrypt SSL certificate only for the subdomain dev.digitaldocblog.com.

patrick@h2866085:/etc/nginx$ sudo certbot --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx

Which names would you like to activate HTTPS for?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: digitaldocblog.com
2: dev.digitaldocblog.com
3: www.digitaldocblog.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 2
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for dev.digitaldocblog.com
Waiting for verification...
Cleaning up challenges
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/dev-reverse-proxy

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/dev-reverse-proxy

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully enabled https://dev.digitaldocblog.com

You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=dev.digitaldocblog.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/dev.digitaldocblog.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/dev.digitaldocblog.com/privkey.pem
   Your cert will expire on 2021-05-24. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

patrick@h2866085:/etc/nginx$

Then I check the new directory and the files in /etc/letsencrypt.

patrick@h2866085:/etc/letsencrypt$ pwd
/etc/letsencrypt
patrick@h2866085:/etc/letsencrypt$ sudo ls -l live
[sudo] Passwort für patrick: 
insgesamt 8
drwxr-xr-x 2 root root 4096 Feb 23 13:54 dev.digitaldocblog.com
drwxr-xr-x 2 root root 4096 Feb 23 06:33 digitaldocblog.com
patrick@h2866085:/etc/letsencrypt$ sudo ls -l live/dev.digitaldocblog.com
insgesamt 4
lrwxrwxrwx 1 root root  46 Feb 23 13:54 cert.pem -> ../../archive/dev.digitaldocblog.com/cert1.pem
lrwxrwxrwx 1 root root  47 Feb 23 13:54 chain.pem -> ../../archive/dev.digitaldocblog.com/chain1.pem
lrwxrwxrwx 1 root root  51 Feb 23 13:54 fullchain.pem -> ../../archive/dev.digitaldocblog.com/fullchain1.pem
lrwxrwxrwx 1 root root  49 Feb 23 13:54 privkey.pem -> ../../archive/dev.digitaldocblog.com/privkey1.pem
-rw-r--r-- 1 root root 682 Feb 23 13:54 README
patrick@h2866085:/etc/letsencrypt$ 

Directory Setup for node applications

I run my node applications on my server in /var/www/node directory. The node directory is owned by root.

All files of my Node Dev Applications will be copied to /var/www/node/dev using the user patrick. This directory is owned by patrick . According to the configuration in dev-reverse-proxy all http requests coming in for server name dev.digitaldocblog.com will be passed to localhost 127.0.0.1 server port 3030. All dev applications must listen on localhost 127.0.0.1 server port 3030.

All files of my Node Prod Application will be copied to /var/www/node/prod also using the user patrick. The prod directory is also owned by the user patrick. According to configuration in prod-reverse-proxy all http requests coming in for server names digitaldocblog.com and www.digitaldocblog.com will be passed to localhost 127.0.0.1 server port 3000. Prod application must listen on localhost 127.0.0.1 server port 3000.

patrick@h2866085:/etc$ cd /var
patrick@h2866085:/var$ ls
backups  cache  lib  local  lock  log  mail  opt  run  spool  tmp  www
patrick@h2866085:/var$ cd www
patrick@h2866085:/var/www$ ls -l
insgesamt 4
drwxr-xr-x 2 root root 4096 Feb 20 12:46 html
patrick@h2866085:/var/www$ sudo mkdir node
patrick@h2866085:/var/www$ ls -l
insgesamt 8
drwxr-xr-x 2 root root 4096 Feb 20 12:46 html
drwxr-xr-x 2 root root 4096 Feb 23 08:53 node
patrick@h2866085:/var/www$ cd node
patrick@h2866085:/var/www/node$ ls -l
insgesamt 0
patrick@h2866085:/var/www/node$ sudo mkdir prod
patrick@h2866085:/var/www/node$ sudo mkdir dev
patrick@h2866085:/var/www/node$ ls -l
insgesamt 8
drwxr-xr-x 2 root root 4096 Feb 23 08:56 dev
drwxr-xr-x 2 root root 4096 Feb 23 08:56 prod
patrick@h2866085:/var/www/node$ sudo chown patrick:patrick dev
patrick@h2866085:/var/www/node$ sudo chown patrick:patrick prod
patrick@h2866085:/var/www/node$ ls -al
insgesamt 16
drwxr-xr-x 4 root    root    4096 Feb 23 08:56 .
drwxr-xr-x 4 root    root    4096 Feb 23 08:53 ..
drwxr-xr-x 2 patrick patrick 4096 Feb 23 08:56 dev
drwxr-xr-x 2 patrick patrick 4096 Feb 23 08:56 prod
patrick@h2866085:/var/www/node$

MongoDB Community Server Installation

Most node applications also interact with a database. My preferred database is MongoDB. Go on the MongoDB website and read more about the free MongoDB Community Server. To read how to install the mongoDB in your environment go to the MongoDB Documentation Site.

I select the installation instructions of the MongoDB Community Server for my Ubuntu Linux 18.04 bionic LTS system.

Then I Import the public key used by the package management system to verify the package source.

$ wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -

Then I create the list file /etc/apt/sources.list.d/mongodb-org-4.4.list for my version of Ubuntu.

$ echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list

I reload the local package database with apt-get update and install a specific release including all relevant component packages with apt-get install such as

  • server

  • shell

  • mongos

  • tools

    $ sudo apt-get update $ sudo apt-get install -y mongodb-org=4.4.4 mongodb-org-server=4.4.4 mongodb-org-shell=4.4.4 mongodb-org-mongos=4.4.4 mongodb-org-tools=4.4.4

Mongodb can be started, restarted etc. with the following commands.

:$ sudo service mongod status

:$ sudo service mongod start

:$ sudo service mongod stop

:$ sudo service mongod restart

After the installation the mongoDB server must be started.

patrick@h2866085:~$ sudo service mongod status
● mongod.service - MongoDB Database Server
   Loaded: loaded (/lib/systemd/system/mongod.service; disabled; vendor preset: enabled)
   Active: inactive (dead)
     Docs: https://docs.mongodb.org/manual

patrick@h2866085:~$ sudo service mongod start
patrick@h2866085:~$ sudo service mongod status
● mongod.service - MongoDB Database Server
   Loaded: loaded (/lib/systemd/system/mongod.service; disabled; vendor preset: enabled)
   Active: active (running) since Tue 2021-02-23 08:49:14 CET; 3s ago
     Docs: https://docs.mongodb.org/manual
 Main PID: 27830 (mongod)
   CGroup: /system.slice/mongod.service
           └─27830 /usr/bin/mongod --config /etc/mongod.conf

Feb 23 08:49:14 h2866085.stratoserver.net systemd[1]: Started MongoDB Database Server.
patrick@h2866085:~$

Then I create the admin user myUserAdmin against the admin db.

patrick@h2866085:~$ mongo
MongoDB shell version v4.4.4
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("8df6776d-4228-43f5-9222-65893d6f146c") }
MongoDB server version: 4.4.4
---
The server generated these startup warnings when booting: 
        2021-02-23T08:49:15.047+01:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
        2021-02-23T08:49:15.718+01:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
        2021-02-23T08:49:15.718+01:00: You are running in OpenVZ which can cause issues on versions of RHEL older than RHEL6
---
---
        Enable MongoDB's free cloud-based monitoring service, which will then receive and display
        metrics about your deployment (disk utilization, CPU, operation statistics, etc).

        The monitoring data will be available on a MongoDB website with a unique URL accessible to you
        and anyone you share the URL with. MongoDB may use this information to make product
        improvements and to suggest MongoDB products and deployment options to you.

        To enable free monitoring, run the following command: db.enableFreeMonitoring()
        To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
> use admin
switched to db admin
> db
admin
> db.createUser({ user: "myUserAdmin", pwd: "yourAdminPassword", roles: [{ role: "userAdminAnyDatabase", db: "admin" }, {"role" : "readWriteAnyDatabase", "db" : "admin"}] })
Successfully added user: {
"user" : "myUserAdmin",
"roles" : [
{
"role" : "userAdminAnyDatabase",
"db" : "admin"
},
{
"role" : "readWriteAnyDatabase",
"db" : "admin"
}
]
}
> db.auth("myUserAdmin", "yourAdminPassword")
1
> show users
{
"_id" : "admin.myUserAdmin",
"userId" : UUID("6b32b520-090b-411b-9569-4ecc70109707"),
"user" : "myUserAdmin",
"db" : "admin",
"roles" : [
{
"role" : "userAdminAnyDatabase",
"db" : "admin"
},
{
"role" : "readWriteAnyDatabase",
"db" : "admin"
}
],
"mechanisms" : [
"SCRAM-SHA-1",
"SCRAM-SHA-256"
]
}
> exit
bye
patrick@h2866085:~$

Then I open /etc/mongod.conf file in my editor an add the following lines to the file if it does not already exist security: authorization: enabled.

#security:
security:
  authorization: enabled

Then I restart the mongoDB service.

patrick@h2866085:/etc$ sudo service mongod restart

I create a new database dev-bookingsystem and the Db-User myUserDevBookingsystem as the Db-owner.

patrick@h2866085:/etc$ mongo
MongoDB shell version v4.4.4
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("48d8e3cb-8076-47cb-8166-8e48cfae3d9a") }
MongoDB server version: 4.4.4
> use admin
switched to db admin
> db.auth("myUserAdmin", "yourAdminPassword")
1
> use dev-bookingsystem
switched to db dev-bookingsystem
> db.createUser({ user: "myUserDevBookingsystem", pwd: "yourDbUserPassword", roles: [{ role: "dbOwner", db: "dev-bookingsystem" }] })
Successfully added user: {
"user" : "myUserDevBookingsystem",
"roles" : [
{
"role" : "dbOwner",
"db" : "dev-bookingsystem"
}
]
}
> db
dev-bookingsystem
> exit
bye

Then I create a default collection in dev-bookingsystem.

patrick@h2866085:/etc$ mongo
MongoDB shell version v4.4.4
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("d3494272-8651-4c7e-a781-231f48a60ec4") }
MongoDB server version: 4.4.4
> use admin
switched to db admin
> db.auth("myUserAdmin", "yourAdminPassword")
1
> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
> use dev-bookingsystem
switched to db dev-bookingsystem
> db.runCommand( { create: "col_default" } )
{ "ok" : 1 }
> show collections
col_default
> show dbs
admin              0.000GB
config             0.000GB
dev-bookingsystem  0.000GB
local              0.000GB
> db
dev-bookingsystem
> exit
bye
patrick@h2866085:/etc$

I do the same thing for my production database prod-digitaldocblog.

patrick@h2866085:~$ mongo
MongoDB shell version v4.4.4
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("2402f47f-d1dd-4399-931e-b327ea1cf4b4") }
MongoDB server version: 4.4.4
> use admin
switched to db admin
> db.auth("myUserAdmin", "yourAdminPassword")
1
> show dbs
admin              0.000GB
config             0.000GB
dev-bookingsystem  0.000GB
local              0.000GB
> use prod-digitaldocblog
switched to db prod-digitaldocblog
> db.createUser({ user: "myUserProdDigitaldocblog", pwd: "yourDbUserPassword", roles: [{ role: "dbOwner", db: "prod-digitaldocblog" }] })
Successfully added user: {
"user" : "myUserProdDigitaldocblog",
"roles" : [
{
"role" : "dbOwner",
"db" : "prod-digitaldocblog"
}
]
}
> db
prod-digitaldocblog
> show dbs
admin              0.000GB
config             0.000GB
dev-bookingsystem  0.000GB
local              0.000GB
> db
prod-digitaldocblog
> db.runCommand( { create: "col_default" } )
{ "ok" : 1 }
> show collections
col_default
> show dbs
admin                0.000GB
config               0.000GB
dev-bookingsystem    0.000GB
local                0.000GB
prod-digitaldocblog  0.000GB
> exit
bye
patrick@h2866085:~$

Now I can connect each app with their database with the following string.

mongodb://<youruser>:<yourpassword>@localhost/<yourdatabase>

Deploy the production node app

To deploy my node app into production I first copy the package.json file into the /var/www/node/prod directory and run npm install to install all dependencies. The directory node_modules now exists in my /var/www/node/prod directory.

patrick@h2866085:/var/www/node/prod$ ls -l
insgesamt 116
-rw-r--r--   1 patrick patrick   615 Jan 18 14:36 package.json
patrick@h2866085: npm install
patrick@h2866085:/var/www/node/prod$ ls -l
insgesamt 116
drwxrwxr-x 220 patrick patrick 12288 Feb 23 13:15 node_modules
-rw-r--r--   1 patrick patrick   615 Jan 18 14:36 package.json
-rw-rw-r--   1 patrick patrick 67705 Feb 23 13:15 package-lock.json
patrick@h2866085:

Then I copy all the application files on the server into the directory ****/var/www/node/prod .

patrick@h2866085:/var/www/node/prod$ ls -al
insgesamt 132
drwxr-xr-x  10 patrick patrick  4096 Feb 23 13:17 .
drwxr-xr-x   4 root    root     4096 Feb 23 08:56 ..
drwxr-xr-x   3 patrick patrick  4096 Feb 23 13:16 app
drwxr-xr-x   2 patrick patrick  4096 Feb 23 13:16 config
-rw-------   1 patrick patrick   167 Feb 19  2020 .env
-rw-r--r--   1 patrick patrick    33 Feb 19  2020 .env.example
drwxr-xr-x   2 patrick patrick  4096 Feb 23 13:16 middleware
drwxr-xr-x   2 patrick patrick  4096 Feb 23 13:16 modules
drwxrwxr-x 220 patrick patrick 12288 Feb 23 13:15 node_modules
-rw-r--r--   1 patrick patrick   615 Jan 18 14:36 package.json
-rw-rw-r--   1 patrick patrick 67705 Feb 23 13:15 package-lock.json
-rw-r--r--   1 patrick patrick  1281 Mai 17  2020 prod.digitaldocblog.js
drwxr-xr-x   2 patrick patrick  4096 Feb 23 13:17 routes
drwxr-xr-x   7 patrick patrick  4096 Feb 23 13:17 static
drwxr-xr-x   2 patrick patrick  4096 Feb 23 13:17 views
patrick@h2866085:/var/www/node/prod$ nano .env

I edit my environment variables in the .env file. Here I configure in particular the server port and the database connection string.

patrick@h2866085:/var/www/node/prod$ cat .env
port= 3000
host=127.0.0.1
mongodbpath=mongodb://<youruser>:<yourpassword>@localhost/<yourdatabase>
jwtkey=<yourjwtkey>
patrick@h2866085:/var/www/node/prod$

Finally I start the production application with PM2.

patrick@h2866085:/var/www/node/prod$ pm2 start prod.digitaldocblog.js