Deploying Meteor on a Production Server
It's been a while since I last wrote here, hopefully I could get back to my weekly routine.
In the last month or so I've been working heavily with Meteor.
When it came to the stage of deploying to a test server, I decided to use a simple node server, without an extra layer of Nginx/Apache. Normally, to build a Meteor application, you run:
1
meteor build
After extracting, you'll have to run node main.js to start the application. This approach has 2 problems:
- Dependencies - Some npm packages will have to be installed via running npm install under programs/server, this adds yet another step that has to be done for deployment.
- No Logs - If we just run the application with node main.js we get no feedback on logs, there's also a problem of the server being shutdown if the parent shell is closed.
Seeing these potential problems, I've decided to build a script for deploying a Meteor application:
1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
PATH_TO_APP='../app'
echo "Building Meteor App to a directory..."
( cd ${PATH_TO_APP}; meteor build --directory . )
echo "Getting NPM packages for project"
( cd ${PATH_TO_APP}/bundle/programs/server; npm install )
echo "Copying start_server.sh and stop_server.sh scripts to app"
cp st*.sh ${PATH_TO_APP}/bundle/
echo "Creating tar.gz file (in the parent folder)"
( cd ${PATH_TO_APP}; tar -zcvf ../bundle_$(date +"%d_%m_%Y_T%H_%M_%S").tar.gz bundle )
echo "Removing Bundle Directory..."
rm -rf ${PATH_TO_APP}/bundle
As you can see, some commands are in parentheses, this spawns a sub-shell so we don't actually change the folder globally (more info). Let's go over this line-by-line (excluding the echo messages):
- PATH_TO_APP='../app' - Define path to application (where the .meteor folder is located)
- ( cd ${PATH_TO_APP}; meteor build --directory . ) - Build meteor app to a directory instead of a file
- ( cd ${PATH_TO_APP}/bundle/programs/server; npm install ) - Install dependencies
- cp st*.sh ${PATH_TO_APP}/bundle/ - Copy the start_server.sh and stop_server.sh scripts (more on those later)
- ( cd ${PATH_TO_APP}; tar -zcvf ../bundle_$(date +"%d_%m_%Y_T%H_%M_%S").tar.gz bundle ) - Compress the file to a tar.gz, with the date & time added to the filename
- rm -rf ${PATH_TO_APP}/bundle - Remove the bundle/ directory we generated earlier
This script works pretty well, and you end up having a more complete package. I have a separate scripts folder parallel to the application folder (this is why the PATH_TO_APP is ../app), this folder also holds the 2 running scripts: start_server.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
NODE_EXEC="nodejs"
if [ "$1" = "debug" ]; then
NODE_EXEC="node-debug"
echo 'Starting Server in Debug Mode...'
else
echo 'Starting Server...'
fi
PORT=3000 \
MONGO_URL=mongodb://localhost:27017/meteor \
ROOT_URL=http://localhost:3000/ \
METEOR_SETTINGS="$(cat settings.json)" \
nohup $NODE_EXEC main.js > /var/log/nodejs.log 2>&1 &
echo $! > server_id.txt
Once again, line-by-line (excluding echo):
- NODE_EXEC="nodejs" - Define the node executable file (could be node/nodejs)
- if [ "$1" = "debug" ]; then - If the the first argument is "debug" (i.e. sh start_server.sh debug), run node-debug instead
- PORT=3000 \ - Define port of the application to be 3000
- MONGO_URL=mongodb://localhost:27017/meteor \ - Define Mongo URL (for DB)
- ROOT_URL=http://localhost:3000/ \ - Define Root URL
- METEOR_SETTINGS="$(cat settings.json)" \ - Define a settings file (if you're not using a custom one, feel free to remove this)
- nohup $NODE_EXEC main.js >/var/log/nodejs.log 2>&1 & - Run the server with nohup (thus not closing if parent shell closes), move all output to /var/log/nodejs.log (make sure you have writing access to the file, otherwise choose another one)
- echo $! > server_id.txt - Get the Server's Process ID (PID) and write it into server_id.txt (so stop_server.sh knows which PID to kill)
stop_server.sh
1
2
3
#!/bin/bash
echo 'Stopping Server...'
kill `cat server_id.txt`
Not much to explain here, we're basically killing the PID that was written by start_server.sh.
Since this is a pretty complete deployment package, I decided to offer it on a git repository.
I'd love to get some improvements going on, feel free to fork/add issues!