Deploying a Node.js site to Vultr
Tags: beginners, node, devops
Published: 2020-09-04 05:51:03

There are plenty of different tools and services that you can use to deploy your projects these days. Lately I've been heading into Heroku and AWS territory, but a service I really like using for simple projects is Vultr.

## So what is Vultr?

Vultr is a server hosting platform, but let's put that into context. I assume you got to this blog post because you are trying to work out how to deploy a Node.js project. So at the moment, you type something like `npm start` and your server starts running and you can view your project. Vultr is a company that has many physical computers lying in wait for you to use. You "upload" your project onto one (or more) of those computers, tell them how to start the server, and bam, you can see your project running on the internets, rather than just locally on your machine.

## What's the difference between Vultr and other cloud hosting services? ☁️

Look, basically nothing. On a surface level you could pick anyone of the major competitors (Digital Ocean, AWS, Azure, Google Cloud) and probably get done what you want to get done. The question though is do you need the extra bells and whistles? For my portfolio website I don't want or need anything fancy, e.g. I don't need it to auto scale to use more resources when it's busy, because at most there are only ever 2 people on my site anyway 🤷‍♀️. What I do need is the bare bones, and clear documentation on how to set that up. I might also consider Digital Ocean, because they are basically interchangeable in my eyes (in fact the first time I set a project up on Vultr, I was flicking between both Vultr and Digital Ocean documentation).

However the reason I would use Vultr rather than Heroku for this kind of site, is because Heroku has a bit of magic that takes care of a lot of steps for you, but with Vultr you have to set up and configure everything. You might be thinking, "🤔 Huh? Why is that a good thing?" This is usually where someone says "oh because it gives you lots of control over every step", what does that mean? Yea I didn't know either, but now that I have been using Vultr for a number of years, I get it.

For the longest time I just couldn't understand how the process of deploying things works. "Put it on the cloud"....ok. "Dynamically spins up servers"....cool story. "Set up https"...all right.... When I started deploying this on Vultr I suddenly understood what all those things mean, what talks to what, and more importantly when I inevitably screw something up (or it just breaks all by itself, because that happens) I now actually know what part is broken and what to frantically Google to fix it.

## I'm convinced, let's do this! 👊

Hurrah!

### Prerequisites

- Have a Node.js website in a Git repository and ready to go.
- Have a domain name purchased and ready to use.

### Step 1

Create an account with Vultr. Preferably you'll use [this link](https://www.vultr.com/?ref=7658295) so that I can get $10 credit from you signing up (come on, you're reading this for free, so help a peep out!).

### Step 2

Once you are signed up, logged in and on your dashboard, go to the Products page and find the button that says "Deploy New Server".

This will take you to a page that looks something like this:

![Screenshot of settings page for new server setup on Vultr.](https://dev-to-uploads.s3.amazonaws.com/i/fgq9hczr1oasay2bnkg4.png)

Now there are obviously a lot of options here (recall the rambling earlier), so pick what is suitable for you, or just follow me if you have no idea what that is. There will be different commands for different operating systems, so I suggest you pick the same option as me for that (Ubuntu).

I'm going to select the following settings:

1. Cloud Compute
2. Sydney (pick the option that is closest to you)
3. 64 bit OS
4. Ubuntu 20.04x64 (latest at time of writing)
5. 25 GB - this is the smallest. If you don't know what you need, just pick this one since it is the cheapest, and if it turns out that's not enough then it is easy to upgrade it later.
6. Leave the next few fields blank, up to server hostname. Give it a name that is meaningful to your and your project, for example, for my portfolio site mine is called "personal-website"

Click "Deploy Now" and it will take you back to your dashboard where you will see the new server is busy being installed.

![Screenshot of dashboard on Vultr showing a server installation progress.](https://dev-to-uploads.s3.amazonaws.com/i/en0ip43lxw52pwimh1ju.png)

Wait for it to finish installing (a few minutes at most).

### Step 3

Click on the server that you just created, it will take you to an overview page for that server. Copy the IP address for the server.

Then open up terminal and type the following to log into the server as the root user, (replacing `<ip address>` with the IP address that you copied:

```bash
$ ssh root@<ip address>
```

You'll be prompted for a password, use the password provided on the overview page in Vultr.

### Step 4

Create a new user. This is the username and password that you'll use to log into the server from here on in, so make sure to pick something sensible (and even better, store it in your password manager if you use one).

```bash
$ adduser <my-new-user>
```

After entering your password you can leave the other prompts blank.

### Step 5

Add the new user to a group and give sudo access:

```bash
$ usermod -aG sudo <my-new-user>
```

Logout and log back in as the new user:

```bash
$ logout
$ ssh <my-new-user>@<ip address>
```

### Step 6

Update any package info on the server then install Nginx:

```bash
$ sudo apt-get update
$ sudo apt-get install nginx 
```

Nginx is what is going to do the heavy lifting for getting people to your site. Every time someone comes to your site, Nginx is going to intercept that request, then talk to your Node.js server to work out what to send back. (The term to look up for more information is "reverse proxy"). There are a few other things it is going to do to, but that's the main task that we are interested in for the scope of this tutorial.

### Step 7

Next we're going to tweak the configuration of the firewall on the Ubuntu server so that Nginx can do it's thing, and so that you can SSH back into your server:

```bash
$ sudo ufw allow 'Nginx HTTP'
$ sudo ufw allow 'OpenSSH'
$ sudo ufw enable
$ systemctl status nginx
```

The last command will give a few lines of output, you just need to check that it says `active (running)` in there somewhere.

If you have any issues with Nginx in the next few steps, here are some handy commands to stop, start, restart and reload Nginx:

```bash
$ sudo systemctl stop nginx
$ sudo systemctl start nginx
$ sudo systemctl restart nginx
$ sudo systemctl reload nginx
```

### Step 8 🔒

This is the step where we set up SSL certificates, i.e. this is where we'll make a connection between your domain name and your server, and also set it up to use HTTPS.

First, install certbot:

```bash
$ sudo apt install certbot python3-certbot-nginx
$ sudo nano /etc/nginx/sites-available/default
```

This will open a file in the nano text editor. There are a lot of comments in the file, you are looking for the line that says:

```bash
server_name _;
```

Replace the underscore with your domain name, so your end result will look something like:

```bash
server_name mydomain.com www.mydomain.com;
```

Save and exit the file.

Verify the file is all good and reload:

```bash
$ sudo nginx -t
$ sudo systemctl reload nginx
```

Modify the Nginx configuration:

```bash
$ sudo ufw allow 'Nginx Full'
$ sudo ufw delete allow 'Nginx HTTP'
```

At this point you will need to set the DNS records in your domain registrar.

Set the A Record Host to `@` and the Value to the IP address for your server (the one you copied way back in step 3).

Set the CNAME record Host to `www` and the Value to your domain name with a `.` suffix, e.g. `mydomain.com.`

Create the SSL certificate:

```bash
$ sudo certbot --nginx -d <mydomain> -d www.<mydomain>
```

When prompted, select 2 to set it to automatically redirect to https.

### Step 9

Install Node:

```bash
$ cd ~/
$ curl -sL https://deb.nodesource.com/setup_10.x -o nodesource_setup.sh
$ sudo bash nodesource_setup.sh
$ sudo apt-get install nodejs
$ sudo apt-get install build-essential
```

### Step 10

Clone your repository and navigate into it:

```bash
$ git clone https://github.com/<my-username>/<repo-name>.git
$ cd <repo-name>
```

Install your dependencies and make sure the server can run:

```bash
$ npm install
$ npm start
```

### Step 11

Install and set up pm2. pm2 is responsible for making sure your site keeps running.

```bash
$ sudo npm install -g pm2
$ pm2 start <server file>.js
$ pm2 startup systemd
```

<server-file>.js is the name of the file that runs your server, (commonly just `server.js`).

```bash
$ pm2 startup systemd
```

A command will be given in the output from the above command, run that command. (It will looks similar to the following)

```bash
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u server --hp /home/server
```

### Step 12

Back to Nginx!

Open the Nginx configuration file we were working in earlier:

```bash
$ sudo nano /etc/nginx/sites-available/default
```

Replace the location block with:

```bash
location / {
	proxy_pass http://localhost:<your port (probably 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;
}
```

Test all is well and restart:

```bash
$ sudo nginx -t
$ sudo systemctl restart nginx
```

### Step 13 🎉

Done! You should now be able to go to your domain in any web browser and see your site!

### Debugging

If you have any issues, then I highly recommend the tutorials from Vultr ([here](https://www.vultr.com/docs/category/node-js/)) and DigitalOcean ([here](https://www.digitalocean.com/community/tutorials)).

### Updating

When you want to update your website in future, all you need to do is pull the latest changes from git and then restart pm2:

```bash
$ git pull
$ pm2 restart <app name>
```