Sitemap

Setting up a public Parse instance in 2020 with parse-server and parse-dashboard.

12 min readApr 16, 2020

--

Overview

These instructions aim to guide an individual through the process of setting up and installing parse-server with parse-dashboard attached and ultimately have them both running on a GCS instance.

Introduction and Purpose

I find it somewhat infuriating that I have to go on fiverr, find a capable backend dev, send them my requirements and then pay them ~$150USD to deploy a GCS instance that simply runs two npm packages for me. Ive googled, binged and I’ve even asked Jeeve a few times and can’t find anything that serves my needs.

As a mobile engineer, I rely heavily on services like Parse and Firebase for a large number of projects that I work on. It therefore seems reasonable to me that learning how to deploy Parse on the cloud is a worthwhile investment of my time and will save me a tonne of money in the long run! It’s also a fresh burst of sanity in these Covidian times.

It’s important to note that these instructions are specific to GCS however are quite generic and depending on your experience level with the platform you use, you should be able to follow them on other platforms like AWS.

System Requirements

Instructions

I’m going to assume that you’ve got your instance setup on your hosting platform of choice with access to an SSH terminal and the correct port access configured as mentioned above. If you’re not using GCS and can’t figure out how to configure port access for your provider please google “Configure port access on PROVIDER_NAME instance”.

👉🏼 STEP 1. Configure DNS Records

Although not required until the very end of this tutorial, we’re going to configure our DNS records now so that we can get it out of the way and not have to go in and out of our DNS provider console. You’ll need to set your DNS records as follows on your domain name.

Name: (No name, leave it blank)
Type:
A
TTL:
3600
Target:
Instance’s External IP Address

Name: www
Type:
A
TTL:
3600
Target:
Instance’s External IP

👉🏼 STEP 2. Switch to Root User

Now let’s move over to our instance. Open up an SSH terminal. Likely to be a controversial topic for most proper backend/fullstack developers, we’re going to switch to the root user of our instance for the entirety of this tutorial. This, as far as I know, doesn’t open up any security issues however might lead to issues down the track when trying to configure different scripts and tasks.

sudo bash

👉🏼 STEP 3. Install NGINX

Before installing NGINX, we have to update our server repositories using the apt packaging system.

sudo apt-get update
sudo apt-get install nginx

At the end of the installation process, NGINX is started automatically. The web server should already be up and running. We can check with the following command:

sudo systemctl start nginx

When you have your server’s IP address or domain, enter it into our browser’s address bar

http://server_domain_or_IP

You should be presented with the default NGINX landing page

👉🏼 STEP 4. Configure Domain Name

For the purposes of this tutorial, we will use example.com as our domain name. Please make sure when following the tutorial below that you substitute all usages of example.com with your domain. Failure to do so will lead to your site simply failing to load. With all that being said, let’s create the home directory and index file for our domain.

sudo mkdir /var/www/example.com
sudo mkdir /var/www/example.com/html
sudo echo "<h1>Hello</h1>" > /var/www/example.com/html/index.html

Once that is done, we need to create an NGINX configuration file

sudo nano /etc/nginx/sites-available/example.com

You’re now inside an editor called ‘nano’. It’s the devil. Don’t even try to comprehend it. I promise you, it’s the devil and you don’t want to go down that rabbit hole.

server {
listen 80;
listen [::]:80;
root /var/www/example.com/html;
index index.html index.htm index.nginx-debian.html;

server_name example.com www.example.com;

location / {
try_files $uri $uri/ =404;
}
}

Save the file by pressing Ctrl+O then Enter/Return to confirm. Finally press Ctrl+X to exit nano. Now take a minute to thank whatever god you thank that you made it through the nightmare that is nano.

We’ll now enable the file by creating a link from it to the sites-enabled directory

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

Now we’ll test our NGINX config for any syntax errors and we’ll restart the service to make sure that the changes we’ve made are enabled.

sudo nginx -t
sudo systemctl restart nginx

NGINX should now be serving content on our domain name. We can test this by visiting our domain in any web browser and we should see the html file we created earlier.

At this point you might be getting a 403 error or a 404 error even. What this means is that the index.html file doesn’t exist and needs to be manually created or there are issues with your firewall. This is too subjective for me to help with so I’d implore you to persist and use the internet to help resolve that issue before moving forward.

👉🏼 STEP 5. Setup SSL

LetsEncrypt/Certbot is a free service that generates auto-renewable, valid and perhaps most importantly, industry accepted, 90 day SSL certificates. The first step in setting this up is adding the Certbot PPA to our list of repositories.

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update

Now we’ll install Certbot to our server along with actually installing the certificates. When installing the certificates you’ll be asked a series of questions. Press yes/accept to all of them (email sharing question optional)

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

The Certbot packages on your system come with a cron job or systemd timer that will renew your certificates automatically before they expire. You will not need to run Certbot again, unless you change your configuration. You can test automatic renewal for your certificates by running this command

sudo certbot renew --dry-run

We can test that the SSL certificate was installed successfully by visiting SSL labs to verify everything worked.

👉🏼 STEP 6. Install NodeJS

The first step in setting this up is adding the NodeJS PPA to our list of repositories in order to get access to its contents. We want to make sure we’re in our home directory when we do this so that we’re installing Node to the right place.

cd ~
curl -sL https://deb.nodesource.com/setup_12.x -o nodesource_setup.sh

Now lets run the install/setup script we just downloaded

sudo bash nodesource_setup.sh
sudo apt-get install nodejs

After all is said and done, we should now have nodejs installed along with npm. We can confirm this by checking the installed versions as follows

nodejs -v
npm -v

Finally, we need to make sure that we’re not going to run into npm issues later on with certain packages that require a few extra packages. We can avoid this by running the following command

sudo apt-get install build-essential

👉🏼 STEP 7. Install MongoDB

The Ubuntu package management tools ensure package consistency and authenticity by verifying that they are signed with GPG keys. The following command will import the MongoDB public GPG key

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10

Create a source list file for MongoDB in /etc/apt/sources.list.d/mongodb-org-3.4.list list file

echo "deb http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list

Now let’s update the local repository and install MongoDB

sudo apt-get update
sudo apt-get install -y mongodb-org --allow-unauthenticated

We need to create a unit file, which tells systemd how to manage a resource. Most common unit type, service, determine how to start or stop the service, auto-start etc. Create a configuration file named mongodb.service in /etc/systemd/system to manage the MongoDB service

sudo nano /lib/systemd/system/mongodb.service

This will open a nano editor with a blank where we can copy the following into

# Unit contains the dependencies to be satisfied before the service is started.
[Unit]
Description=MongoDB Database
After=network.target
Documentation=https://docs.mongodb.org/manual
# Service tells systemd, how the service should be started.
# Key `User` specifies that the server will run under the mongodb user and
# `ExecStart` defines the startup command for MongoDB server. [Service]
User=mongodb
Group=mongodb
ExecStart=/usr/bin/mongod --quiet --config /etc/mongod.conf
# Install tells systemd when the service should be automatically started.
# `multi-user.target` means the server will be automatically started during boot.
[Install]
WantedBy=multi-user.target

Once that’s done, we’re going to update the systemd service and then start the mongod service. Note here that we’re using monogod and NOT monogodb.

sudo systemctl daemon-reload
sudo systemctl start mongod

With those two services restarted and back up and running we want to make sure that MongoDB is running on port 27017 and that the service is starting properly.

sudo netstat -plntu
sudo systemctl status mongod

We should see the following output in our terminal (highlighted line shows mongod running on port 27017). Note that the service name is also mongod here.

Now we can also make MongoDB start when the system starts up to avoid having to restart the service if we need to restart our instance or it ever goes down.

sudo systemctl enable mongod

👉🏼 STEP 8. Connect and Configure MongoDB

With MongoDB installed we now need to setup a user and database for our parse server to connect to. So, quite simply let’s execute the following commands.

mongo
use admin

We are now going to create a user that has access to the ‘admin’ db within Mongo. You’ll need to change the values here for security purposes but for the sake of this tutorial i’ll use admin and password as the username and password for the MongoDB user.

db.createUser({user:"admin", pwd:"password", roles:[{role:"root", db:"admin"}]})

Now exit from the MongoDB shell by pressing Ctrl + C and let’s enable authentication on MongoDB.

sudo nano /lib/systemd/system/mongod.service

We should now be in an editor again (nano 🤮). We need to add the “quiet” and “auth” options to ExecStart as follows

ExecStart=/usr/bin/mongod --quiet --auth --config /etc/mongod.conf

Save the file (Ctrl+O, Enter/Return, Ctrl+X), and continue to restart MongoDB to apply the changes we made. We’ll also attempt to connect to the database in order to confirm that we have a working user with database permissions. Once you’ve confirmed that the user works and that database access works. You can use Ctrl+C to exit the MongoDB shell.

sudo systemctl restart mongod
sudo systemctl daemon-reload
mongo -u admin -p password –-authenticationDatabase admin
show dbs

👉🏼 STEP 9. Install PM2

We need to install pm2 to make parse-server and parse-dashboard start automatically and handle logging for us.

npm install -g pm2

👉🏼 STEP 10. Install Parse-Server

If you’ve made it this far, give yourself a little clap 👏. You’re through the hardest parts of this tutorial and the rest is now quite trivial. We’ll now create an empty directory for our parse-server files and then move into it.

cd ~
sudo mkdir /var/www/parse-server
cd /var/www/parse-server

Now we need to initialise a node project and fill in the fields. The important thing to note here is that you can’t create a project with the same name as the module/package you’re installing. So in this example, don’t call your project parse-server because we're installing a package called parse-server. This isn't just a best practice thing. Installation will fail and you'll need to go through the yucky nano editor to change the name of your project.

npm init

Once you’ve created a project, you can install the parse-server and the (optional but highly recommended) Mailgun adapter. The Mailgun adapter lets you use the Mailgun service to send forgot password emails and other emails from your system.

npm install --save express parse-server @parse/simple-mailgun-adapter

Now let’s create an index.js file using nano (🤮) and fill it with the contents of the file below. Remember to change the example.com references to your own domain name. You’ll also need to change the databaseURI parameter to match the username/password values you used when creating the MongoDB user.

sudo nano index.js

For security reasons, you should also really change the masterKey and appId values to random strings (UUID is good).

const express = require('express');
const ParseServer = require('parse-server').ParseServer;
const app = express();
const fs = require('fs');
const port = 1337;
const options = {
key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem', 'utf8'),
cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem', 'utf8')
};
const api = new ParseServer({
appName: 'MyParseServer',
databaseURI: 'mongodb://username:password@localhost:27017/admin',
appId: 'myAppId',
masterKey: 'myMasterKey',
serverURL: 'https://example.com:1337/parse',
cloud: __dirname + '/cloud/main.js',
emailAdapter: {
module: '@parse/simple-mailgun-adapter',
options: {
// The address that your emails come from
fromAddress: 'MyEmail <noreply@example.com>',
// Your domain from mailgun.com
domain: 'mailgun.example.com',
// Your private API key from mailgun.com
apiKey: 'key-xxxx'
}
},
liveQuery: {
classNames: ["User"]
},
maxUploadSize: "200mb",
}
);
app.use('/parse', api);
const httpsServer = require('https').createServer(options, app);
httpsServer.listen(port, () => {
console.log('parse-server is running on port ', port);
});
ParseServer.createLiveQueryServer(httpsServer);

Save the file (Ctrl+O, Enter/Return, Ctrl+X) once you’ve made all the necessary changes.

We now need to make sure that cloud code runs properly on our server. To do this we need to create the /cloud directory and the main.js file and then proceed to start parse-server.

sudo mkdir cloud
sudo nano main.js

Now just save main.js as a blank file by using the Ctrl + O followed by Enter/Return and then Ctrl + X. We can now start our parse-server.

pm2 start index.js --name=parse-server

You can make sure that parse-server starts automatically on system reboots by executing the follwoing commands

pm2 startup
pm2 save

To restart parse-server or to view the logs you can use the following commands respectively

pm2 restart parse-server
pm2 logs parse-server

I’d recommend executing both now and making sure that the logs are not ballooning with errors. Any errors at this point, point directly to an issue with the setup and you should not be proceeding until these are fixed. Provided that you have no errors though, you should be able to access the database using a Parse SDK by pointing it to https://example.com:1337/parse. You’ll notice that pointing your browser at that link automatically throws an error. This is intended and means that we have some form of security.

👉🏼 STEP 11. Install Parse-Dashboard

We’ll now create an empty directory for our parse-dashboard files and then move into it.

cd ~
sudo mkdir /var/www/parse-dashboard
cd /var/www/parse-dashboard

Again, we need to initialise a node project and fill in the fields.

npm init

Once you’ve created a project, you can install the parse-dashboard package.

npm install --save express parse-dashboard

Now let’s create an index.js file using nano (no puke emoji, the joke’s old now) and fill it with the contents of the file below.

sudo nano index.js

Remember to change the example.com references to your own domain name. You’ll also need to change the values for appId and masterKey to the values you setup when creating the parse-server. You’ll also need to setup the user and pass values. You’ll use these when actually logging into the dashboard. Optionally you can finish off by changing the appName value, this is purely visual but useful if you’re deploying multiple apps into one dashboard.

const express = require('express');
const ParseDashboard = require('parse-dashboard');
const fs = require('fs');
const port = 4040;
const options = {
key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem', 'utf8'),
cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem', 'utf8')
};
const dashboard = new ParseDashboard({
apps: [{
'serverURL': 'https://example.com:1337/parse',
'appId': 'myAppId',
'masterKey': 'myMasterKey',
'appName': 'MyParseServer',
'iconName': 'icon.png'
}],
users: [{
'user': 'mylogin',
'pass': 'mypasswd'
}],
iconsFolder: __dirname + '/icons'
}, false);
const app = express();
app.use('/', dashboard);
const httpsServer = require('https').createServer(options, app);
httpsServer.listen(port, () => {
console.log('parse-dashboard is running on port ', port);
})

Save the file (Ctrl+O, Enter/Return, Ctrl+X) once you’ve made all the necessary changes and then proceed to start parse-server.

pm2 start index.js --name=parse-dashboard

You can make sure that parse-server starts automatically on system reboots by executing the follwoing commands

pm2 startup
pm2 save

To restart parse-server or to view the logs you can use the following commands respectively

pm2 restart parse-dashboard
pm2 logs parse-dsahboard

I’d recommend executing both now and making sure that the logs are not ballooning with errors. Any errors at this point, point directly to an issue with the setup and you should not be proceeding until these are fixed. Provided that you have no errors though, you should be able to access the dashboard using a modern browser by pointing it to https://example.com:4040.

Resources

In addition to all the above mentioned resources, you can also find a tonne of resources across the web. Keep in mind that facebook originally maintained and built parse for consumers so it’s an extremely well documented and still maintained (by the community) platform.

✉️ Contact Information

In case you run into any issues, you can reach out via email. I usually respond within 48 business hours. Please be mindful that I have a day job that I don’t like to interrupt with things that should be done in personal time. I’ll get to your emails in due course.

🎙 If you have any questions or suggestions, please message me at b.stillitano95@gmail.com.

--

--

Brandon Stillitano
Brandon Stillitano

Written by Brandon Stillitano

Senior Mobile Engineer. Proficient in Swift & Kotlin. Check out my GitHub at https://github.com/bstillitano

Responses (2)