Codementor Events

Git Push > Deploy Your Meteor App

Published Mar 21, 2018Last updated Mar 21, 2020

meteor.png

Meteor is such an amazing framework that you almost forget what comes next when your app is built and ready to be out in the wild. Deploy.
Deploying meteor apps on low cost (ideally free for at least prototype phase) has been one of main pain points in the ecosystem over the period of last few years, ever since meteor's own free hosting shut down. As a result, a number of tools and platforms tried to fill the void with a lot of interesting approaches such as mup, heroku buildpacks, meteor-now etc. All of those seem to work just fine until they don't (just like any other software out there). So, I decided to look under the hood and figure out a very minimal setup for deploying meteor apps on my DigitalOcean droplet that I already have lying around for tinkering with my toy apps. Turns out, it's not that complicated, thanks to stackoverflow and the amazing people of the interwebz.

What we're going to do

At the end of this post, we will have a solid git push based deploy strategy for our meteor app which will be accessible to the world through https(SSL). Come with me and I shall show you the way.

Primary tools:

Any server running ubuntu should do for this setup to work as long as you have ssh access on it. The tools we're going to need are mainly:

That's it, like I said, minimal setup.

Copy pasting commands

If you're like me, ctrl+c -> ctrl+v is your best friend, ain't no shame in that. However, when you're copy pasting any command/code from this post, make sure you consider a few things:

  • I'm using foysal as the username and 199.199.19.19 as my server ip so make sure you replace these two with your respective username and server ip when running any command containing those.
  • I'm calling my meteor app allende. Feel free to replace that with your own app's name.
  • I'm using allende.com as my app's domain name so make sure you replace that with your actual domain name that you own and want to associate with your app.

Installing the necessary tools

If you already have a server somewhere, chances are, you already have most of the tools already installed. In case you're missing any of the tools from the list above, just click the link and follow the tutorial to install it on your server.

Configure build through git remote

Assuming you have everything from the list installed, first thing we're going to do is, setup git remote on our server. Login on the server with your ssh user account. Then run the following commands in your [server user's] home dir to create a bare repo for our remote:

mkdir repos && cd $_ 
mkdir allende.git && cd $_ 
git init --bare
touch hooks/post-receive && nano $_

This will open up the nano editor on your terminal. Paste the following bit of shell script in the file and ctrl+x out of the file to save it.

#!/bin/sh
. ~/.profile
REPO_DIR="$HOME/repos/allende.git"
APP_DIR="$HOME/meteorapps/allende"
OUTPUT_DIR='$HOME/meteorapps/allende-built'
BUILD_OUTPUT="$OUTPUT_DIR/allende.tar.gz"
APP_NAME='allende'
# architechture might need to be changed if your system is not 64bit
LOCAL_ARCHITECTURE='os.linux.x86_64'

export PORT=3000
export NODE_ENV='production'
export ROOT_URL='https://allende.com'
export MONGO_URL='mongodb://localhost:27017/allende'

while read oldrev newrev refname
do
    branch=$(git rev-parse --symbolic --abbrev-ref $refname)
    echo "Received $branch branch"

    if [ "master" = "$branch" ]; then
        # Copy the latest code from the git repo to the app directory
        echo "Copying Code Into $APP_DIR"
        git --work-tree=$APP_DIR --git-dir=$REPO_DIR checkout -f $branch
        
        # navigate into the app directory then build and run the app from there 
        echo "Building Meteor App"
        cd $APP_DIR
        meteor npm i
        METEOR_SETTINGS="$(cat settings.json)" meteor build --server-only $OUTPUT_DIR --architecture $LOCAL_ARCHITECTURE
        forever stop $APP_NAME
        tar -xzf $BUILD_OUTPUT -C $OUTPUT_DIR
        rm $BUILD_OUTPUT
        
        # install npm deps
        cd $OUTPUT_DIR/bundle/programs/server
        npm i --save bcrypt
        npm i --production

        # go back to main dir
        cd ../../

        echo "----------STARTING APP----------"
        # run the app
        forever start --id $APP_NAME main.js
        sleep 5
        forever list
    else
    	echo "Deploy is disabled for $branch branch"
    fi
done

This seems a bit intimidating but in plain english, it says: When our server receives any new git commit on the master branch, copy the latest code content into our application directory then build the application using meteor build tools that outputs a plain nodejs app which can be run using forever.js.

Now if you read through the shell script, you'll notice a few things that don't yet add up, so let's fix those.

We need a directory for our app's code. Let's create that in the user's home directory:
mkdir ~/meteorapps && mkdir ~/meteorapps/allende
The script assumes you have a settings.json file in your app dir but as safety measure, you should never commit your settings.json file in your git repo. So, normally, that file won't exist when git copies your content. Which is why you need to copy and paste the content of that file on the server:
touch ~/meteorapps/allende/settings.json && nano $_
Now copy file content from your local machine to the server editor and save the file.

Next thing you'll notice is, meteor build command outputs a zipped file so we create a directory to put that file in:
mkdir ~/meteorapps/allende-built
Also, if you haven't created a separate database in mongodb for your app, make sure you do that. In this script, we're assuming the db name is allende to match the app's name.

We're using https://allende.com as ROOT_URL of our app, replace this with your actual domain name and we'll setup the domain name with apache virtual host in a bit.

Now the last thing we need to do is, install forever.js and meteor.js on our server. It's as simple as :

npm i -g forever
curl https://install.meteor.com | /bin/sh

We're not done yet but at this point, we can test out what we've built so far. To check it out, go back to your local terminal and from within your app add your app's remote to your local app and push the master branch to the server remote:

git remote add production foysal@199.199.19.19:repos/allende.git
git push production master

Git will now start outputing the build steps on your terminal. Check for any error. If everything ends well, you'll see the forever list output telling you where your app's log is and the current state of the app. We're now ready to setup apache to serve our app through a custom domain name.

Configure apache

Assuming you have your custom domain's DNS configured to point to your server IP, we will setup a virtual host with reverse proxy that will server our app on that custom domain name. For this step, you'll need sudo access. Create a config file for our virtualhost : sudo touch /etc/apache2/sites-available/allende.com.conf && sudo nano $_ then paste the following content in the editor :

<VirtualHost *:80>
  ServerName allende.com

  # Handle proxy stuff
  ProxyPreserveHost On
  ProxyRequests off
  ProxyPass / http://199.199.19.19:3000/
  ProxyPassReverse / http://199.199.19.19:3000/

  <Proxy *>
  	Order deny,allow
  	Allow from all
  </Proxy>
</VirtualHost>

Now save and exit the file. This configuration, simply pipes any access to allende.com into our meteor app running on port 3000 of our server. Now, we need to enable this config so that apache recognizes this new config file and since this config uses proxy stuff, we will need a special module enabled on apache. Following commands on the server terminal will set you up for that:

sudo a2enmod proxy_http
sudo service apache2 restart
sudo a2ensite /etc/apache2/sites-available/allende.com.conf
sudo service apache2 reload

Ta-Da! we now have our app accessible at http://allende.com. Go ahead and try it out on your browser.
That's great and all but I promised you a secure site with green lock icon of approval from chrome. So, let's enable https on apache using certbot.

Going https:// with certbot

Certbot (previously known as letsencrypt) is a godsent piece of software that makes enabling ssl on any server a piece of cake, a free cake moreover. If you've followed the setup tutorial for certbot, all you need is one single command to make your domain accessible through https:
sudo certbot --apache -d allende.com
This will ask you to choose a couple options such as force https and configure all the necessary stuff for you, right away. As soon as that's done, you just need to reload your apache config one last time and be on your merry way: sudo service apache2 reload. Now open your app on the browser using https:// and BAM! you should see that sweet sweet green on the address bar Selection_037.png

But wait, there's more

I'm sorry but this is quite important, however, you may not need this bit. One very quirky thing we need to fix on apache is, making sure our app accepts websocket connections. If you open your app on chrome and inspect the network calls in the devtools, you'll notice that the websocket calls are failing with 400 or 200 status code. Which is an issue on apache's side and it may not even occur with a latest version. However, if you do see this, make sure you apply this fix, if not, Huzzah for you!
Now this one is bit dependant one what you did on the certbot config step. If you've setup forced https, you'll need this new config to be put inside the /etc/apache2/sites-available/allende.com-le-ssl.conf file but if you didn't optin for forced https, you will be editing the /etc/apache2/sites-available/allende.com.conf. Now open the proper file in sudo mode and paste the following code in the VirtualHost config tags:

  RewriteEngine on
  RewriteCond %{HTTP:Upgrade} websocket$ [NC]
  RewriteCond %{HTTP:Connection} Upgrade$ [NC]
  RewriteRule .* ws://199.199.19.19:3000%{REQUEST_URI} [P]

After pasting this content, simply reload your apache config. Now just to be sure, go back to your browser's devtool and confirm that the websocket network calls are not failing anymore.

Protip

If you're cheap bastard like me and run your meteor apps on $5 DO boxes, you might run into issues when meteor build runs out of memory. The following commands will enable a swap memory that might help your build actually finish:

sudo swapoff -a
sudo dd if=/dev/zero of=/swapfile bs=1M count=1024
sudo mkswap /swapfile
sudo swapon /swapfile

Congratulations

giphy.gif

Go ahead, have a sip of that sweet sweet vitory wine or do a lap around the office, you've earned it. You're now a proud host of your own meteor app.

Now everytime you build a new feature or fix a bug, all you need is to run git push production master on your local repo and your changes will go live on the server in a jiffy.

Just FYI, I totally understand if you think any other option, paid or unpaid, is better than this and there are a lot of them so I'll, in fact, encourage you to explore some of them and may be write about how to use it.

Credits

As I've mentioned in the begining of this post, all of this is achieved through scraping the interwebz and connecting bits and pieces from here and there but a few sources has to be mentioned :

If you need a hand building an app or fixing a bug in an existing one or if you found any problem following this post feel free to reach out.

Discover and read more posts from Foysal
get started