Julien Tregoat

@julientregoat

Start to Finish: Deploying a React App on DigitalOcean

While deploying an app recently, I experienced many frustrations with the various nuances that accompany deployment, most of which were made even more difficult by the lack of centralized information about it online. Of course, this is a result of the many ways that deployment can be done (meaning that this guide is only one way to do things).

This guide is designed to be a start to finish guide for someone creating a React single page application using DigitalOcean, with every important detail. We’ll serve it with NGINX as a reverse proxy (and explain why you want to do this), PM2 as a process manager, and Serve to actually handle the serving of the app. Most of the concepts here with regards to setting up your DigitalOcean server are transferrable to the AWS EC2 platform once you have shell access to your server. The concepts here can also be utilized for other SPAs, an Express.js based server, etc.

This guide will assume you already have a DigitalOcean account and have pointed your domain name registrar’s name servers to DigitalOcean. For info on changing your name servers, check out this article from DigitalOcean: How to Point to DigitalOcean Nameservers From Common Domain Registrars

If you don’t already have an account, here’s a mutually beneficial referral link to get you started with a $10 credit (enough for about 2 months of server time for their cheapest server).

Step 1: Create a Droplet

Droplets are DigitalOcean’s cleverly named servers. We’re going to create one. Click the big green create button at the top of the screen.

Distributions

DigitalOcean is known for having ‘One-click apps’ that come shipped with everything you need, but for the sake of explicitly adding what we need, we’ll start with an empty distribution.

There are multiple options as far as distributions. Ubuntu is pretty common, and I like it, so we’ll go with that! Currently, there is a new LTS version on the horizon — 17.10 — but we’re going to stick with 16.04 since that’s what I’m used to working with.

Size, Location, Options

You don’t need much power for a simple React app that’s meant to serve as a portfolio or starting point for your deployment endeavors. For our purposes, I’ll select the $5 1GB / 1 vCPU / 25GB SSD / 1TB transfer option.

For location, pick whatever is closest to you or wherever you expect your users to be.

There are some really awesome options you have available, such as private networking (e.g. a server that can only communicate with your other DO servers) and weekly backups, but we don’t need that for now.

SSH Keys

This one is really important! SSH (or Secure SHell) is essential for direct networking with your server in a safe way. Other articles cover this, but there’s room for confusion that we should avoid.

First, check if you have existing SSH keys. If you are a programmer, you’ve probably encountered this or at the very least already have one. To do this, open up Command Prompt / Terminal and enter ls -al ~/.ssh . This command is listing everything in the .ssh directory within your home directory (which you may not see, since it’s hidden by default), with the -a flag showing all files (meaning, the hidden ones with your normal not-hidden ones) and -l displaying more information about each subdirectory within it. If you have a pair of keys already, they’ll likely be called id_rsa and id_rsa.pub. If so, you can skip down a paragraph.

You’ll get some fancy art when you make your key.

If you don’t have an SSH key, we’ll need to create a new one. Create it using the command ssh-keygen -t rsa. Follow the prompts — you’ll need to specify the file (leave it as the default id_rsa option by leaving your response blank) and a passphrase (leave it blank again if you’d like). For more details on this process, check out this other handy DigitalOcean article.

Now that you have your SSH key, cd ~/.ssh and open up id_rsa.pub. This is your public key, meaning the key that services can use to verify you are you when connecting. id_rsa is a PRIVATE key — don’t pass this one out. Copy the contents of the entire id_rsa.pub file, go back to your DigitalOcean ‘Create Droplet’ tab in your browser, click ‘New SSH Key’ under ‘Add Your SSH Keys’, and paste it in there. Name the key something related to your computer, so you can identify where the key is held if needed.

You can now name your droplet! It’s probably best to name them after what you plan on calling the website, since if you deploy more it could start to get confusing. Hit the big green ‘CREATE’ button again!

Step 2: Setting up the Droplet

After a minute from hitting ‘CREATE’, your droplet should be ready. Head over to the droplets section of DigitalOcean, where you’ll find a list of your droplets. Copy the IP address next to your droplet’s name on the list. Since you included an SSH key, we won’t need a password from your email or anything to login!

Connecting Via SSH

Back to your command prompt / terminal. To connect to the server over SSH, we’ll use the ssh command (duh!), and specify the user on the account at IP address provided. Since we’re starting out as root, the command will look like this: ssh root@[YOUR IP HERE]. There will likely be a security prompt to ensure you want to continue (yes, you do), and a password if you specified one when creating the SSH key, but that’s it. And like that, you’re in! You’ll know it when your prompt changes to reflect the user and server that you’re on, e.g. root@[SERVER NAME].

Changing the Default User

A major point of security when setting up a server is to change the user with which you’re connecting. In the same way that one should be explicit in their code to minimize erratic outcomes, one should be explicit in user privileges granted. The account you’re logged in as will be the account that your applications use, meaning if anything were compromised, your entire system would be in jeopardy as a result. It doesn’t take much more than a `rm -rf` with too large of a scope to destroy your server.

In our SSH connection to our server, lets create a new user now: adduser [USERNAME]. You’ll be prompted for a password (do this) and some other information that isn’t really necessary. We also want to make sure this user has access to the sudo group — meaning the powers of root — when necessary. To do this, we’ll do the following: usermod -aG sudo [USERNAME].

  • usermod here is the command used to modify user accounts and privileges
  • The -a flag means append / add, meaning. This can only be used with -G, which…
  • -G enables you to select which group you’re adding the user to.
  • sudo is the group AND level of privileges! (Fun fact — sudo comes from ‘super user do’)

If you did it correctly, you should be able to prepend sudo to any command to run it with root privileges! Let’s test it quickly. Switch over to your new user using su [USERNAME] and try sudo echo ‘Hello!'. You don’t need root powers to do this, but it’ll prompt you for your password, which is an indication that you did it correctly. If you’re not able to access it, try this process again.

Setting Up SSH For the New User

We also want to be able to SSH into this user, instead of having to go through root. Let’s set this user up for that. Logged in as the new user, enter the following commands:

  • cd ~ — to get back to your home directory.
  • mkdir .ssh — Making the directory for SSH, hidden as a result of the `.` prefix
  • touch .ssh/authorized_keys — Creating the file with which your public key will be stored, so the user can recognize you.
  • chmod 700 .ssh — Changing permissions of the directory; it’s hidden from others, but you still have access.
  • chmod 644 .ssh/authorized_keys — Changing permissions of the file; it can be read by all, but only changed by the current user.

That was a lot! But we still have the most important step left: adding your public SSH key. Hopefully you still have your `id_rsa.pub` file’s contents still copied into your clipboard, but if not, go back to your computer and copy it. Now, we’ll edit the file the file with VIM! VIM is intimidating to me as a relatively new programmer, but once you know a few of the basic commands, it’s actually quite convenient. Let’s use the following command to edit your authorized keys from your home directory on your server: vim .ssh/authorized_keys.

WARNING: I’m no VIM expert, so I’m probably using it inefficiently. Don’t hate me! It works.

Currently, we are just viewing the .ssh/authorized_keys file. To edit it, we’ll press i. Now, paste in your key. Next, we can use [CTRL]+ [ to exit insert mode. Next, enter :wq to write (save) your file and quit it (on their own, :w handles the writing and :q handles the quitting). SSH is all set! This user will recognize you when you use your private key to SSH into it.

Now that we have our new user setup, there’s no reason for us to be working with root! Enter exit into your terminal to exit the SSH prompt. Now, let’s log in with your new username to your server via SSH! It’s similar to before, but instead of root, we’ll use your new username: ssh [USERNAME]@[IP ADDRESS].

Installing Environment Dependencies

We’ll need to install node to get working with our React app. The following will do the job to get the latest version of Node at the time of writing, 9.10, running. This is the latest version, not the LTS release, so make sure this is OK for you before proceeding. This article goes more in depth about install options via package managers and other versions.

curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo apt-get install -y build-essential

This installs Node, npm, and the build tools that come in handy for npm.

Now would be a good time to install any other dependencies you may need for your app, but for a vanilla React app, you’re probably fine with this.

Once Node & npm are installed, we’re going to need to install two tools:

  • npm i -g pm2 — installs PM2, aka Process Manager. If the name didn’t tip you off, it’s going to help us handle running our app in the background of your server.
  • npm i -g serve — installs Serve, a very handy app that serves up a single page on a server. Since single page apps are only on one page and modify that page, this works for React.

Step 3: Setting Up Your React App On Your Server

From here on, it should be smooth sailing since the rest is stuff you’ve probably already encountered in your day to day! You’re going to need to clone your React app to the server from Github. If you used create-react-app to bootstrap this project, you can run npm run build to package your assets into a minified version that will be more efficient for production. This will create a build folder in the root directory of your project.

Now, for the main event! Run pm2 serve build. Here, PM2 will use Serve to display your webpage. I believe the default port that Serve uses is 5000, so you should be able to see your project running at [YOUR PUBLIC IP ADDRESS]:5000. If it’s not working, check pm2 list — it will let you know what the status of all of your processes are, how many restarts each process has had, etc. While you’re at it, get familiar with the PM2 command line arguments — it’ll come in handy when it comes to managing your app.

If it is working, great! We have just a little more work to do.

Step 4: Setting Up A Reverse Proxy with NGINX

Before we get started with the how, here’s the why:

A reverse proxy is a buffer between your server and the rest of the internet, in addition to your standard firewall. Think of it like this — instead of talking to various resources around your server, there is an intermediary that checks every request, gets the information requested from the server, and sends it back out. In our case, we have PM2/Serve putting up your webpage on port 5000 (or whatever it is on your computer). Instead of exposing that port, your app stays guarded and unexposed behind this buffer.

The use of PM2 to serve the site isn’t explicitly necessary. Another option would be to use NGINX to serve the static files. But I wanted to do this 😁.

Let’s make sure you have NGINX installed: sudo apt-get install nginx.

NGINX is a service that works in tandem with the default Ubuntu firewall, which can be accessed with ufw. It should show up if you type ufw app list. There should be 3 options:

  • Nginx-Full — Enables both ports 22 and 88 for HTTP and HTTPS access, respectively.
  • Nginx-HTTP — Enables port 22 for HTTP only.
  • Nginx-HTTPS — Enables port 80 & HTTPS only.

Installing an SSL certificate and setting up HTTPS is out of the scope of this post, but feel free to check out this solid DigitalOcean post on it. Anyway, enable whichever one is appropriate for you. We’ll assume you just want to get going, so use sudo ufw allow 'Nginx HTTP’ to allow as few ports as necessary.

Next, we need to setup the configuration of your NGINX service. Using cd /etc/nginx/sites-enabled should get you to a directory with a default file in it. vim default to open this file in VIM. Don’t forget: i to insert, [CTRL] + [ to get out of insert mode, :w to write/save your file, and :x to exit.

You’re going to want to ALMOST copy and paste everything I’ve listed in the block below to your default file, but there’s a couple of things we need to personalize.

  • server_name — Call it whatever you want to refer to your whole server as.
  • upstream — This can be named whatever as well, but it’s probably best to name it after the app you’re pointing it to. Make sure that the port address is correct — you want to point to your local port that your app is running on. The upstream itself is exactly as it sounds — one of your apps that live ‘upstream’ from your proxy, and is guarded as a result.
  • proxy_pass — You’re going to pass in the name of your upstream here, since it’s essentially forwarding all requests here at the moment.
upstream my_nodejs_upstream {
 server 127.0.0.1:5000;
 keepalive 64;
}
server {
 listen 80;
 server_name my_nodejs_server;
 root project_root;
 
 location / {
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header Host $http_host;
 proxy_set_header X-NginX-Proxy true;
 proxy_http_version 1.1;
 proxy_set_header Upgrade $http_upgrade;
 proxy_set_header Connection “upgrade”;
 proxy_max_temp_file_size 0;
 proxy_pass http://my_nodejs_upstream/;
 proxy_redirect off;
 proxy_read_timeout 240s;
 }
}

Once you’ve updated this /etc/nginx/sites-enabled/default file to look like this, you’re at the finish line! Now cross it.

Enter sudo ufw enable to make sure the default firewall is enabled. Then, sudo service nginx start to get NGINX running. Once more, your app should be accessible from your IP address, except you won’t need to specify a port. Specifying a port shouldn’t get you anything.

Congratulations, and thanks for sticking through this tutorial! Your app is now fully functional once more. It should be accessible via the IP address, as well as through your domain name if you setup your name servers correctly!

If you see any errors or typos, or just generally want to get into contact, hit me up!

Topics of interest

More Related Stories