Run Meteor App as a Service on Ubuntu

It is really easy to run Meteor App in development mode. It’s a bit harder when you want to run it as native node.js App. I’ll show you how to run Meteor App as a system service on Ubuntu using systemd.

Install node.js

Meteor 1.3 is using NodeJs v 0.10.43. Incoming Meteor 1.4 version will use node.js 4.4.4. The fastest way to install and use NodeJS in any version is using n NPM package. You can have node.js it running in seconds by typing:

$ apt-get install curl git build-essential
$ curl -L http://git.io/n-install | bash
Now it’s good time to log out and in again to a system console, so n and node commands will work.

Then you can install any version of NodeJs:

$ n 0.10.43

You can verify installed version:

$ node --version

Install MongoDB

This recently got a little bit complicated because now there is no APT repository working with Ubuntu 16.04. I’ll show you how to compile it from source — it’s not that hard. Alternatively, you can run MongoDB in a Docker Container.

Compile MongoDB on Ubuntu

Firs you need a machine with 45 GB free space, 2GB RAM and 4GB swap to compile. After the build is done you can free 39GB occupied by source and precompiled modules.

$ apt-get install -y scons build-essential 
$ apt-get install -y libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev
$ git clone https://github.com/mongodb/mongo.git
$ cd mongo
$ git checkout r3.3.6
$ scons all --jobs 4
In above example, I’m using latest stable release 3.3.6. You can check if there is a newer version on Github.com.

It will take about 15 minutes to complete on 4 CPU machine. You can adjust jobs parameter to number of CPU’s available. After build finishes install MongoDB in your system:

$ scons --prefix=/opt/mongo install

This will create /opt/mongo/bin directory with all mongo executables inside. You can symlink them to /usr/local/bin to make them available system-wide:

$ cd /usr/local/bin
$ ln -s /opt/mongo/bin/mongo
$ ln -s /opt/mongo/bin/mongod
$ ln -s /opt/mongo/bin/mongoperf
$ ln -s /opt/mongo/bin/mongos

Wee need to create user, create data, log and PID dir and then set correct permissions:

$ useradd mongodb
$ mkdir -p /var/lib/mongodb
$ mkdir -p /var/log/mongodb
$ mkdir -p /var/run/mongodb
$ chown mongodb:mongodb /var/lib/mongodb
$ chown mongodb:mongodb /var/log/mongodb
$ chown mongodb:mongodb /var/run/mongodb

Compile Mongo Tools

Additional mongo tools now are rewritten in Golang:

$ git clone https://github.com/mongodb/mongo-tools.git
$ cd mongo-tools
$ git checkout v3.3
$ apt-get install golang-go
$ ./build.sh

After build is done you can symlink them to same location as before:

$ ln -s bin/bsondump /usr/local/bsondump
$ ln -s bin/mongodump /usr/local/mongodump
$ ln -s bin/mongoexport /usr/local/mongoexport
$ ln -s bin/mongofiles /usr/local/mongofiles
$ ln -s bin/mongoimport /usr/local/mongoimport
$ ln -s bin/mongooplog /usr/local/mongooplog
$ ln -s bin/mongorestore /usr/local/mongorestore
$ ln -s bin/mongostat /usr/local/mongostat
$ ln -s bin/mongotop /usr/local/mongotop

Run MongoDB as a service

First create MongoDB config file in /etc/mongod.conf:

# Where and how to store data.
storage:
dbPath: /var/lib/mongodb
wiredTiger:
engineConfig:
cacheSizeGB: 1
journal:
enabled: true
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1
# tell mongo to fork
processManagement:
fork: true
pidFilePath: /var/run/mongodb/mongod.pid
replication:
replSetName: meteor

Then create /etc/systemd/system/mongod.service file:

[Unit]
Description=MongoDB Database Service
Wants=network.target
After=network.target
[Service]
LimitNOFILE=200000
TimeoutSec=5min
Type=forking
PIDFile=/var/run/mongodb/mongod.pid
ExecStart=/usr/local/bin/mongod --config /etc/mongod.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
User=mongodb
Group=mongodb
StandardOutput=syslog
StandardError=syslog
[Install]
WantedBy=multi-user.target

Then tell systemd that you created new service, enable and start it:

$ systemctl --system daemon-reload
$ systemctl enable mongod.service
$ systemctl start mongod

To make sure that all needed directories will be in place after system restart create /usr/lib/tmpfiles.d/mongod.conf containing one line:

$ echo "d /var/run/mongodb 0755 mongodb mongodb" > /usr/lib/tmpfiles.d/mongod.conf
It’s good to verify if MongoDB service is starting by rebooting your machine or container.

Configure ReplicaSet for Meteor

Meteor applications benefit from using Replica Set Oplog. To enable this feature go to mongo shell and type:

$ mongo
mongo> rs.initiate({_id: "meteor", members: [{_id: 0, host: "127.0.0.1:27017"}]});
{ "ok" : 1 }
meteor:SECONDARY> (press Enter here)
meteor:PRIMARY>
Make sure _id equals replSetName value from /etc/mongod.conf.

Build Meteor App

Go inside main app directory and type:

meteor build --directory /output/path/here

Once build is complete you have to install required NPM packages:

cd /your/output/path/bundle/programs/server
npm install

Configure systemd service

There was a Linux civil war about startup system. Battle was lost by Init and Upstart and now, hopefully, Systemd will be used in all future releases.

Meteor App Service file — example

Service definition can be put in /etc/systemd/system/yourAppName.service file. Example service definition goes like this:

[Service]
ExecStart=/usr/local/bin/node PATH_TO_YOUR_APP/main.js
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=yourAppName
User=root
Group=root
Environment=NODE_ENV=production
Environment=PWD=PATH_TO_YOUR_APP
Environment=PORT=3000
Environment=HTTP_FORWARDED_COUNT=1
Environment=MONGO_URL=mongodb://127.0.0.1:27017/yourAppDBName
Environment=MONGO_OPLOG_URL=mongodb://127.0.0.1:27017/local
Environment=ROOT_URL=http://your-app-domain.com
Environment='METEOR_SETTINGS={"someSetting": "someValue"}'

[Install]
WantedBy=multi-user.target

I marked things you have to change. Once file is created you have to notify systemd about new content and enable it:

$ systemctl daemon-reload
$ systemctl enable yourAppName.service
$ systemctl start yourAppName

Then you can start / stop / restart your app simply by:

$ systemctl start|stop|restart yourAppName

Troubleshooting and diagnostics

If you have problems running MongoDB or Meteor App always check logs in /var/log/mongo/… and system journal. When you start your new service its output will be visible there:

$ journalctl -f

You may filter messages by service:

$ journalctl -f -u yourAppName
-f stands for follow-mode — it’ll show only most recent messages and will show new entries as they are appended to the journal.
You may omit -f flag then the whole journal will open in vim.
If you add -e flag editor will open at the end of the log.

I tested this solution on Ubuntu 16.04 (xenial) container running on Proxmox 4.2, but it should work also on Ubuntu 15.04 (vivid) and Ubuntu 15.10 (wily).