Skip to content

Instantly share code, notes, and snippets.

@chriswessels
Created September 12, 2013 16:15
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 15 You must be signed in to fork a gist
  • Save chriswessels/6540167 to your computer and use it in GitHub Desktop.
Save chriswessels/6540167 to your computer and use it in GitHub Desktop.
A guide to setting up self-hosted infrastructure for Meteor applications on Ubuntu Server 13.04.

#Meteor and Self-hosted Infrastructure

Meteor is an eye-opening JavaScript framework that runs on both the client and the server, giving developers a revolutionary take on software engineering. If you are not familiar with Meteor, I urge you to visit their website.

##An overview

In this brief gist, I am going to discuss the process of setting up a server (in my case, a VPS) to host Meteor applications.

My experience with Meteor has been brief, however it has not taken much demonstration for me to realise the significance of this stellar framework. Let's jump right in!

Disclaimer: This is by no means a production-ready guide for deploying Meteor applications to self-hosted infrastructure. It should get you started, though.

##The application stack

There are a number of key software components that will run on both your computer (i.e. a development machine) and the server that will be hosting your Meteor applications. These components include:

On the server, I will be using Ubuntu Server 13.04 x86_64. In addition to the components above, the following will also run on Ubuntu:

On your development machine, in addition to the components above, you'll also be running:

##Our application

In this example, we'll be deploying a copy of the Leaderboard example on the Meteor website to your custom infrastructure.

##Preparing the server

###Package updates and additions

Assuming a fresh installation of Ubuntu Server 13.04 x86_64, the first thing to do is ensure that our system is completely up-to-date. SSH into the server as root and run the following commands:

$ apt-get update && apt-get upgrade

Next, let's uninstall some packages that are included with Ubuntu by default that we don't need:

$ apt-get remove apache2 apache2-doc apache2-mpm-prefork apache2-utils apache2.2-bin apache2.2-common

Now that that's done, let's install some packages that we'll be using:

$ apt-get install curl nginx git mongodb

###Add a new user for deployment

It is definitely a good idea for us to run our Node.js instance as a non-root user. Let's create a new user with the username meteor for our purposes:

$ adduser meteor

###Give our new user some power

Upstart, the service manager that we'll be relying on to keep Node.js running, provides a number of system commands for administering services. We're interested in start, stop and restart.

These are the commands that Meteor Deployment Manager uses to manage the Node.js service, so the meteor user should have limited access to use them. We don't want meteor to be able to administer other system services, so let's specify exactly which commands the meteor user is able to execute.

In order to do this, we are going to permit the meteor user to run certain commands using the sudoers file. The sudoers file includes any files that are found within /etc/sudoers.d/, so let's add a new file there. Since we'll be hosting and deploying the Leaderboard example, let's call the file leaderboard.

$ touch /etc/sudoers.d/leaderboard

Next, let's add a rule to this file, explicitly allowing the meteor user to access the following commands:

  1. start leaderboard
  2. stop leaderboard
  3. restart leaderboard

start, stop and restart are all located in the /sbin/ directory.

$ echo "meteor ALL = (root) NOPASSWD: /sbin/start leaderboard, /sbin/stop leaderboard, /sbin/restart leaderboard" > /etc/sudoers.d/leaderboard

###MongoDB

After running apt-get install mongodb above, MongoDB should already be running. All we need to do is add the URL of the local MongoDB instance to the system path so that it is accessible by Meteor.

Run the following command to add the relevant URL to the system path:

$ echo 'MONGO_URL="mongodb://localhost:27017"' >> /etc/environment

###Nginx

Non-root users may not bind to ports lower than 1025. This means that in order to run Node.js directly, we'd need to build it to port 80 as the root user. This poses a number of security threats that are beyond the scope of this gist.

One solution, which offers some other benefits, is to use nginx as a proxy. This solution involves binding nginx to port 80, running as root. Node.js is bound to a port above 1024, and nginx is configured to transparently route any traffic to port 80 through to Node.js.

Decide on a port for your application. I've used port 2000 in this example.

Nginx should be installed and running after we ran apt-get install nginx above. Let's replace the configuration file to suit our requirements. I've uploaded a sample configuration file here. The nginx system configration file is located at /etc/nginx/nginx.conf. After replacing the configration file, we'll restart nginx.

Run the following commands to back up your existing nginx.conf file and download the sample:

$ mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf_backup
$ curl https://gist.github.com/chriswessels/6515786/raw/18f14dc33284e1231055ee632f3ae780d4e4af1b/nginx.conf >> /etc/nginx/nginx.conf

Edit the new configuration file to suit your requirements, then run:

$ service nginx restart

###Adding the Upstart configuration script

Upstart is the service manager that we're going to be using to monitor Node.js. Upstart jobs are configured as files in /etc/init/. I've uploaded a sample configuration script here.

To download it, issue the following command:

$ curl https://gist.github.com/chriswessels/6515635/raw/b5e6ddab49cc465c06b4282734e34d5781fd27f3/leaderboard.conf >> /etc/init/leaderboard.conf

Looking near the top of the file, you will see the following lines:

env APP_NAME='leaderboard'
env PORT='2000'
env ROOT_URL='http://www.your-app.com'
env NODE_BIN='/home/meteor/.nvm/v0.8.24/bin'

You should edit these lines to match your requirements. These configure the instance of Node.js. This sample script will attempt to launch an instance of your application located at /home/meteor/leaderboard/builds/current. This is where Meteor Deployment Manager (MDM) places the latest build.

###Log in as the meteor user

At this point you can logout of your SSH session and log in as the meteor user we just created. This is to prevent unwanted, unintentional file permissions when touching files as root.

###Install Node Version Manager and Node.js

Back? Great. Let's install Node Version Manager (NVM) so that we can maintain multiple versions of Node.js on the server. The install script NVM provides is not sufficient for our purposes, so install NVM using the following commands:

$ git clone https://github.com/creationix/nvm.git ~/.nvm
$ sed -i -e '1i [[ -s $HOME/.nvm/nvm.sh ]] && . $HOME/.nvm/nvm.sh' ~/.bashrc
$ source ~/.bashrc

Now that we've got NVM, let's install Node.js. We're going to install version 0.8.24, because that's what Meteor is tested against at this point. We're also going to make version 0.8.24 the default Node.js.

$ nvm install 0.8.24 && nvm alias default 0.8.24

###Install Meteor and add it to the user PATH

Let's install Meteor:

$ curl https://install.meteor.com | sh

This will succeed, however it will display a warning saying that the meteor user doesn't have privileges to copy the meteor executable into a folder that is in the PATH environment variable. This is fine, because we can add it into the PATH environment variable for the meteor user, rather than adding it system wide.

Run the following command to append /home/meteor/.meteor to your PATH for the meteor user:

$ sed -i -e '1i PATH="$PATH:/home/meteor/.meteor"' ~/.bashrc
$ source ~/.bashrc

At this point you should be able to run the command below with successful output:

$ meteor --version

If you're using Meteorite, now would be a good time to run:

$ npm install -g meteorite

Now that the server is prepared to run Node.js (and therefore Meteor) applications, let's set up the project for hosting and automated deployment.

###Create the project's directory structure

Since we're going to be using MDM on our development computers for automated deployment, we need to set up a server-side directory structure that conforms to what MDM is expecting.

NB: MDM is still in alpha stages, so please look at the project's README file for the latest information.

Using the commands below, we're going to create the directory structure expected by MDM. We're also going to create a log directory that we can use later:

$ cd ~ && pwd
-> /home/meteor
$ mkdir leaderboard && cd leaderboard && pwd
-> /home/meteor/leaderboard
$ mkdir builds log source working && ls
-> builds log source working

Now that we have the directory structure in place, we can check out the source code code for our project. This should be done in the source directory. MDM expects an origin remote to be present.

$ cd source
$ git init
$ git remote add origin https://github.com/chriswessels/meteor-leaderboard-example
$ git pull origin master

...and that's it!

##Setting up your development machine

I'm going to assume that you have the following up and running on your local development machine:

  • Node.js (recommended: via NVM)
  • Git
  • Meteor
  • Meteorite (if your application requires it)

Navigate to your project folder and install MDM using the following commands:

$ cd ~/meteor-leaderboard-example
$ npm install -g meteor-deployment-manager

This will install Meteor Deployment Manager and make it available in your system path as the mdm command.

###MDM deployment configuration file

Once MDM is installed, execute:

$ mdm generate

This will generate a sample deploy.json deployment configuration file in the current working directory.

Edit the file to suit your requirements. Mine looked something like this after I edited it:

{
  "options": {
    "meteorite": false,
    "insecure": false,
    "meteorRelease": "0.6.5.1"
  },
  "environments": {
    "staging": {
      "hostname": "staging.your-app.com",
      "port": 22,
      "username": "meteor",
      "password": "secure-password",
      "deploymentDirectory": "leaderboard",
      "gitBranch": "master",
      "taskName": "leaderboard"
    }
  }
}

taskName should match the Upstart configuration file name (without the .conf extension). deploymentDirectory should be the directory to deploy to relative to ~.

###Deploying with MDM

Whether you're deploying for the first time, or making updates to an application that is already online, MDM makes deploying easy!

  1. Make sure that your latest changes have been pushed to the origin remote via Git.
  2. Run mdm deploy.
$ pwd
-> /Users/chriswessels/meteor-leadership-example
$ git push origin master
$ mdm deploy

MDM will now attempt to connect to your server via SSH and perform the necessary actions to deploy your latest changes. If you get an error when MDM attempts to restart Node.js, try running:

$ mdm start

##Todo

The following sections of this gist need creation or improvement:

  • Blocking port 2000 with UFW on Ubuntu.
  • A more comprehensive todo list.

##Contributions

  1. Fork this gist.
  2. Submit a pull request with a sane commit message.
@jumami
Copy link

jumami commented Feb 8, 2015

where do we install mdm? in local machine or remote?
I installed in local machine and it throught "mdm: command no found" error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment