Deploying node.js on Amazon EC2

Posted on by in Ops, Web

After nearly a month of beating my head against the wall that is hosted node.js stacks — with their fake beta invites and non-existent support — I decided it was time to take matters into my own hands. Amazon Web Service (AWS) offers 12 months of a micro instance for free (as in beer) with 10 GB of disk and 613 MB of memory. This is perfect for an acceptance server running node. All you need to do is sign up with a new email address and provide a credit card. Totally worth it. After 12 months, the price will jump to roughly $15 a month.

I’m a huge fan of Debian and it’s progeny Ubuntu. The guys over at do a great job of providing Amazon Machine Images (ami) that are production ready. I choose to use Ubuntu 10.04 LTS because it will be supported until April of 2015. The 64 bit ami for the us-east region is ami-63be790a. Feel free to choose one that best suits your needs.

You will want to setup your default Security Group (the AWS firewall) to allow inbound port 22 and 80 at a minimum (read this for more information). After your instance is up and running, download the ssh identity file (*.pem) and ssh to your new server as the ubuntu user.

ssh -i <identity>.pem

At this point you will want to update your package list and perform all the upgrades for security purposes.

$ sudo apt-get update
$ sudo apt-get upgrade -y

Shortly we can get down to the business at hand but first we need to install a few build tools.

$ sudo apt-get install build-essential libssh-dev git-core -y

Then we can download and install node (takes about 20 minutes to build):

$ wget
$ tar zxf node-v0.4.11.tar.gz
$ cd node-v0.4.11
$ ./configure
$ sudo make install

Finally we can download and install the node package manager (npm).

$ curl | sudo sh

Now your instance is ready to run a node.js server (node server.js). Yes it is really that easy.


We use teamcity to run all of our tests and automatically deploy to our acceptance server with capistrano. This means you will need a ruby installed as well as a few gems. Bring on the Gemfile:

source ''

gem 'capistrano'
gem 'capistrano-ext'
gem 'bluepill'

Then run a bundle to install all the required gems.

Here is the Capfile:

load 'deploy' if respond_to?(:namespace) # cap2 differentiator
load 'config/deploy' # remove this line to skip loading any of the default tasks

And the capistrano file config/deploy.rb (change it to your EC2 hostname and github repo):

set :stages, %w(acceptance production)
require 'capistrano/ext/multistage'

set :application, "node"
set :user, "ubuntu"
set :host, ""
set :deploy_to, "/var/www/node"
set :use_sudo, true

set :scm, :git
set :repository, ""
set :branch, "development"

set :deploy_via, :remote_cache
role :app, host

set :bluepill, '/var/lib/gems/1.8/bin/bluepill'

default_run_options[:pty] = true

namespace :deploy do
  task :start, :roles => :app, :except => { :no_release => true } do
    run "#{try_sudo :as => 'root'} #{bluepill} start #{application}"

  task :stop, :roles => :app, :except => { :no_release => true } do
    run "#{try_sudo :as => 'root'} #{bluepill} stop #{application}"

  task :restart, :roles => :app, :except => { :no_release => true } do
    run "#{try_sudo :as => 'root'} #{bluepill} restart #{application}"

  task :create_deploy_to_with_sudo, :roles => :app do
    run "#{try_sudo :as => 'root'} mkdir -p #{deploy_to}"

  task :npm_install, :roles => :app, :except => { :no_release => true } do
    run "cd #{release_path} && npm install"

before 'deploy:setup', 'deploy:create_deploy_to_with_sudo'
after 'deploy:finalize_update', 'deploy:npm_install'

Note that this assumes bluepill is installed as a system gem. And here is the stage file config/deploy/acceptance.rb (don’t forget to add your EC2 hostname):

set :node_env, 'acceptance'
set :branch, 'development'
set :keep_releases, 10

server '', :web, :app, :db, :primary => true

You will need to setup inbound and outbound ssh keys for github and teamcity. Add your teamcity to /home/ubuntu/.ssh/authorized_keys on the EC2 server and copy your teamcity id_rsa to /home/ubuntu/.ssh/id_rsa as well.

For process management I decided to try out bluepill because I’ve found monit to be unruly and I’m sorta in like with ruby. Here is the acceptance.pill:

Bluepill.application("app") do |app|
  app.process("node") do |process|
    process.working_dir = "/var/www/node/current"
    process.start_command = "/usr/bin/env NODE_ENV=acceptance app_port=80 node server.js"
    process.pid_file = "/var/www/node/shared/pids/"
    process.stdout = process.stderr = "/var/www/node/shared/log/node.log"
    process.daemonize = true

    process.start_grace_time = 10.seconds
    process.stop_grace_time = 10.seconds
    process.restart_grace_time = 20.seconds

    process.checks :cpu_usage, :every => 10.seconds, :below => 5, :times => 3
    process.checks :mem_usage, :every => 10.seconds, :below => 100.megabytes, :times => [3,5]

Now we just need to load up the configuration on the server:

$ bluepill load acceptance.pill

You can run a cap acceptance deploy from your dev machine or your CI server and the new code will go out and your node.js process will be restarted. You might have to tweak your authorized_keys to make that happen though. Enjoy.

UPDATE: The alestic AMI ships with no swap space so if you are running memory intensive apps you might want to check here:


  Comments: 19

  1. Seems like a nice cheap acceptance environment. Beware that micros are seriously underpowered.

    • Yeah, I’ve heard some horror stories about CPU stealing as well. Node.js seems to handle it just fine.

      • They’re not really underpowered, and they’re not really “horror stories” — it’s all part of the design.

        Micro instances are intended for “bursty” activities. The situation is, you have some task that happens infrequently that needs CPU, but the majority of the time, the instance is just idle waiting for that task to come along. You could just get a small or medium instance, but that would be inefficient for everyone — for you because you’d be paying mostly for idleness, and for Amazon because they’d be reserving CPU time for you that you’re not using.

        Micro instances are the solution to that. Basically, they can do up to 2 ECU’s worth of processing (the equivalent of a Medium AWS instance), to handle those bursty tasks, but only for a little while. If you use high levels of CPU for too much time, you get throttled hard (that “CPU stealing” you were talking about). There needs to be some consequences or people would use Micros for consistent throughput applications, which defeats their purpose.

        So, if your Node server’s workload is constant, you should be using a Small. If it’s bursty and usually idle, then a Micro should never experience that throttling you’re worried about. I hope that clears things up — Micro instances are one of the most frequently misunderstood pieces of EC2.

        • Adrian — our acceptance environments get little traffic most of the time and when they do it’s pretty bursty. So it works well.

  2. Great guide Ben; really explicit and straightforward.

    I might have to give the node.js a shot.

  3. Great guide. Nice easy setup for an env.

  4. It is always recommended to have a swap partition in place. Read this article:

    As the author says, “While some people are running without swap it just seems like a bad idea. Basically you’re walking around with a gun pointed at your head waiting to go off if you accidentally sneeze.”.

  5. Hey Ben, great writeup, thanks! I’ve had fun playing with the cedar stack at Heroku for a small node thing we worked on recently at Bookrenter, but not in a production capacity yet, time will tell how that plays out but I think we’ll find out soon.

  6. The font, mixes with background, too hard to read.

  7. Great post, I’m a node.js fan … I need to test it on EC², I already done it on Heroku. I have posted an article on my blog about nodejs on ARM processor 🙂 Feel free to read it : Node.JS on Seagate DockStar !


  8. Hi Ben,

    Ubuntu’s working on a tool called Ensemble to make deploying to EC2 much simpler. Here’s an example of deploying node and mongo on EC2.

    We already have a node.js formula so you’ll be able to basically cut out the entire manual set up part of your instructions (like finding AMIs) and get right to work on the important stuff.

  9. Hi, after I ssh into the ec2 instance it asks me for a password… I’m at a standstill, Thanks – James

    • You need to download your identity file from the AWS console and use it when you ssh to the server.

      ssh -i <your identity file> ubuntu@<your server name>

      After that you can setup new accounts or change the ubuntu password.

  10. Thanks for the write-up! This is really helpful for getting started with EC2.

  11. Thanks – very handy & accurate guide.

  12. I found an article for this in german
    I hope this help german readers.

  13. I’ve used some of this information to produce a more complete deploy guide and example files (and not just for Amazon):

  14. N. McAdow, thank you so much for your kind note. I am glad you enjoyed the book, and hearing from you was teairficrlly encouraging. Rest assured, I am laboring away on more projects, and I’m chuffed thinking that you’ll be looking forward to them.

  15. Hi
    I am laboring away on more projects, and I’m chuffed thinking that you’ll be looking forward to them.

Your feedback