Configure A Fresh VPS To Host A Website
Tags: vp, ubuntu, nextjs, mongodb
Published: 2022-07-17 03:25:24



Got a new VPS (Ubuntu 20.04) on Namecheap. It seems that the basic VPS tier on Namecheap offers double CPU, RAM and storage than that of my old basic tier on AWS lightsail.

This journal concisely notes: How to configurate a fresh VPS to host my new website [peiwen.me](https://peiwen.me).

**Reminder:**

Just run these commands once ssh into remote server

```bash
apt update
apt install curl
```

**Notes:**

- `apt` is a Linux package management system
- 'fun' fact: `apt` cmd was introduced in 2014, partly replaced `apt-get` after the release of Ubuntu 16.04, for being faster, friendlier, easier to use

---

## Firstly! `SSH`

- `mosh` is a good mobile shell - highly responsive, gotta be installed both locally and remotely.
- local ssh config for convenience:

  ```bash
  # add info to local file: ~/.ssh/config
  Host <name_your_remote>
    HostName <static_IP>
    User <remote_username>
    IdentityFile <path_to_local_id_rsa>
    IdentitiesOnly yes
  ```

- remote ssh config:

  ```bash
  # local shell
  ssh <user>@<static_IP>

  # remote shell
  mkdir .ssh
  touch authorized_keys
  apt-get install nano # just in case
  apt-get install mosh # just in case
  nano authorized_keys
  # paste the content of the local file 'id_rsa.pub' to this file, save and exit
  ```

- NOW! YOU CAN~
  ```bash
  # local shell
  mosh <name_your_remote>
  ssh <name_your_remote>
  ```

---

## NodeJS

### `nvm`

```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
# might need to reconnect remote for `nvm` command to work properly
```

Note: check the latest `nvm` installation script [here](https://github.com/nvm-sh/nvm)

### `node`

```bash
nvm list-remote
nvm install stable # or pick one from the list
nvm list
node -v
npm -v
npm install --global yarn # recommended
```

Note: if "nvm 'command' not found" - check ['Troubleshooting on Linux Section'](https://github.com/nvm-sh/nvm#troubleshooting-on-linux)

---

## MongoDB

<details>
<summary>Problems with installing v5.0</summary>

🔥 This exact [problem](https://www.linuxfixes.com/2022/03/solved-mongod-does-not-start.html) encountered when I installed v5.0 - `mongod` service won't start. The reason might be:

> the VM’s CPU does not support instructions required by MongoDB 5.0 (such as AVX and SSE) which should be listed in the `CPU flags`:
> `flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr`

Similar issues found:

- [mongodb community forum](https://www.mongodb.com/community/forums/t/setting-up-mongodb-v5-0-on-ubuntu-20-core-dump-status-4-ill/120705/3)
- [stack-overflow](https://stackoverflow.com/questions/68609678/mongodb-result-signal-code-killed-signal-ill)

You might need to install `apt install procinfo` to run `cat /proc/cpuinfo` to checkout your CPU type.

</details>

### [Install MongoDB v4.4 Community Edition](https://www.mongodb.com/docs/v4.4/tutorial/install-mongodb-on-ubuntu/#install-mongodb-community-edition)

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

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

sudo apt-get update

sudo apt-get install -y mongodb-org=4.4.15 mongodb-org-server=4.4.15 mongodb-org-shell=4.4.15 mongodb-org-mongos=4.4.15 mongodb-org-tools=4.4.15
```

### Start and config

```bash
sudo systemctl start mongod
sudo systemctl status mongod
sudo systemctl enable mongod
mongo
mongosh
```

Note: `sudo` can be omitted if you are the `root` superuser

<details>
<summary>Uninstall MongoDB</summary>

```bash
sudo service mongod stop
sudo apt-get purge mongodb-org*
sudo rm -fr /var/log/mongodb
sudo rm -fr /var/lib/mongodb
```

</details>

### Security Checklist

[MongoDB Guide](https://www.mongodb.com/docs/manual/administration/security-checklist/)

### IP Binding to create a replica backup

[Mongo](https://www.mongodb.com/docs/manual/core/security-mongodb-configuration/)

---

## Git

### Installation

```bash
apt update
apt install git
git --version
```

### Setup

To setup username if push to source is needed

```bash
git config --global user.name <username>
git config --global user.email <email>
git config --list # or ↓ directly check gitconfig file
nano ~/.gitconfig
```

This is NOT enough anyway if you were to edit code and push to source, for only SSH communication is allowed with github repos since 2021.

### Clone repo

Clone web repo to `~/web/`, install the deps

```bash
git clone <repo_address>
cd <project>

yarn
# OR
npm i
```

### Configure `.env.local`

```bash
touch .env.local
nano .env.local
```

**`.env.local` content ↓**

```text
MONGODB_URI=<get_this by typing mongosh>
MONGODB_DB=journal

PASS=<some_password>
NEXT_PUBLIC_PASS_TOKEN=<some_JWT_like_token>

NEXT_PUBLIC_REVAL_TOKEN=<some_token_for_revalidation_token>
```

### Build

```bash
yarn build
yarn global add pm2
pm2 start yarn --name "peiwen.me" -- start
pm2 ls

# to start on startup
pm2 startup
# to freeze/save the processes
pm2 save
```

---

## Nginx

### 1. Install, config firewall and enable nginx

```bash
apt install nginx

# firewall
ufw app list

# returns
Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH
  mosh

# enable firewall
ufw allow 'Nginx Full'
ufw allow OpenSSH # important
ufw allow mosh
ufw enable
ufw status

# returns
Status: active
To                         Action      From
--                         ------      ----
Nginx Full                 ALLOW       Anywhere
OpenSSH                    ALLOW       Anywhere
mosh                       ALLOW       Anywhere
Nginx Full (v6)            ALLOW       Anywhere (v6)
OpenSSH (v6)               ALLOW       Anywhere (v6)
mosh (v6)                  ALLOW       Anywhere (v6)

# check your nginx server
systemctl status nginx

# get your IP if you don't already know for some reason
curl -4 icanhazip.com
<IP_address>

# enter this <IP_address> into the browser, you should be able to see the landing page of nginx

# lastely, enable it on every boot
systemctl enable nginx
```

### 2. Setting Up Server Blocks

An nginx `server block` is a file inside folder `/etc/nginx/sites-available/`

```bash
cd /etc/nginx/sites-available
touch <new_domain> # optional
nano <new_domain>
```

Insert content:

```bash
server {
  # the name of your domain
  server_name <new_domain> www.<new_domain>;

  location / {
    # this must stay localhost. The port must be the same as your Next.js project
    proxy_pass http://localhost:3000;
    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;
  }

  # simple headers
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
  add_header X-XSS-Protection          "1; mode=block" always;
  add_header X-Frame-Options DENY always;

  # this will serve the project on port 80
  listen 80;
}
```

### 3. Verify and reload nginx for it to take effect

```bash
# check all working
nginx -t

ln -s /etc/nginx/sites-available/<new_domain> /etc/nginx/sites-enabled/

systemctl reload nginx
```

### PS. Do NOT forget

- UPDATE `host records`: point <new_domain> to server IP ([which record type to choose](https://www.namecheap.com/support/knowledgebase/article.aspx/579/2237/which-record-type-option-should-i-choose-for-the-information-im-about-to-enter/))

Refs:

- [Digital ocean guide](https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-20-04)
- [Nginx Directory Structure](https://wiki.debian.org/Nginx/DirectoryStructure)
- [nginx beginners guide](https://nginx.org/en/docs/beginners_guide.html)

---

## SSL

### 1. Install `certbot` and some preparation

```bash
apt install certbot python3-certbot-nginx

# firewall settings
ufw allow 'Nginx Full'
ufw allow 'Nginx HTTPS'
ufw deny 'Nginx HTTP'
ufw status

# check if the <server_name> is exactly the format "example.com www.example.com"
nano /etc/nginx/sites-available/example.com
```

### 2. Obtain the SSL/TLS Certificate

```bash
# do not forget the '-d' before www.example.com
certbot --nginx -d example.com -d www.example.com
# enter your email...
```

> Once you configure HTTPS, Certbot completes generating the certificate and reloads Nginx with the new settings.
>
> Finally, the output displays that you have successfully generated a certificate and specifies the location of the certificate on your server. `/etc/letsencrypt/live/example.com/fullchain.pem (success)`

Ref: [How to Secure Nginx with Let's Encrypt On Ubuntu 20.04](https://phoenixnap.com/kb/letsencrypt-nginx)

If you open your nginx file now `nano /etc/nginx/sites-available/example.com`, you'll find the extra info has been added:

```bash
server {
  server_name <new_domain> www.<new_domain>;

  location / {
    proxy_pass http://localhost:3000;
    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;
  }

  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
  add_header X-XSS-Protection          "1; mode=block" always;
  add_header X-Frame-Options DENY always;

  # ↓ the following lines are added by Certbot

  listen 443 ssl; # managed by Certbot
  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
  ssl_certificate_key /etc/letsencrypt/live/example.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.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

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

  server_name example.com www.example.com;
  listen 80;
    return 404; # managed by Certbot
}
} # an extra for some reason...
```

```bash
# check certificate's auto-renewal
certbot renew --dry-run
```

### 3. Setup Cronjob to Enable Automatic Certificate Renewal

```bash
# open Crontab
crontab -e
```

Add a line:

```bash
# Schedule it to run daily at a specified time (in this example, it does so at 05:00 a.m.)
0 5 * * * /usr/bin/certbot renew --quiet
```

---

## Maintain

```bash
# in remote <site_folder>
git pull && yarn build && pm2 restart peiwen.me
```

---

## Useful articles 😄

- [Linux Filesystem Tree Overview](https://help.ubuntu.com/community/LinuxFilesystemTreeOverview)
- [Ubuntu manual - file hierarchy](https://manpages.ubuntu.com/manpages/bionic/man7/file-hierarchy.7.html)
- [Add Google Analytics to NextJS App](https://mariestarck.com/add-google-analytics-to-your-next-js-application-in-5-easy-steps/)