# Sunday, 05 December 2010

Update: About one year after writing this article I switched to hosting node.js apps on Heroku. Check it out.

Node.js is an impressively fast and lightweight web server based on the Google V8 Javascript engine. I really enjoy working with node.js for the simple elegance of language parity between client and server. The use of server-side Javascript also means taking advantage of common JS patterns, such as event-driven programming and closures (there's just something reassuring and pure about functional programming on the server that gives me a greater sense of confidence when there are no state dependencies between expressions).

In the spirit of the CloudStock event tomorrow, I set out to install Node.js on an Amazon EC2 instance. Amazon is running a promotion on free EC2 micro instances, otherwise micros can be leased for about $0.02 per hour or ~$20 per month.

Step 1

Signup for Amazon EC2 and click on "Launch Instance" to get started.

Step 2

I prefer the default Amazon Linux machine image, but any Linux distribution should work. The default Linux AMI is stripped down and secure out of the box.

Step 3

Select the type of instance. I recommend starting with a Micro for creating a simple Node sandbox.

Step 4

Accept the default advanced instance options by clicking "Continue"

Step 5

Give your instance a name, such as "Node Sandbox".

Step 6

This is the most critical part of the instance provisioning process. If a key pair has not already been defined in EC2, create one by entering a key name then downloading the resulting *.pem file to the local desktop.

Step 7

If this is your first time at Amazon, you'll need to generate a firewall profile for use by the Node Sandbox instance. Allow SSH (port 22) and HTTP (port 80).
We'll initially be hosting Node on port 8080, which unfortunately is not configurable in the instance request wizard. Make a mental note that we'll be coming back to security groups in step 10 to allow port 8080.

Step 8

Review the request and press "Launch" to fire up your Linux virtual machine. Amazon says it could take several minutes to provision the VM. In my experience, the micro instances have only taken seconds.

Step 9

Confirm the new instance is running. Copy the "Public DNS" URL of the instance into a text editor; you'll be using it frequently in the next steps. (Note: Make sure to copy the Public DNS and not the Private DNS, which is only used for internal EC2 connections).

Step 10

Select the instance then click on Security Groups. Modify the group to allow tcp traffic over port 8080. That's it for EC2 configuration.

Step 11

SSH. The remaining steps all require SSH access to the newly provisioned EC2 instance. This article uses the bash terminal on Apple OSX for demonstration.

Remote access for root user is not enabled. Instead, the user "ec2-user" is made available with sudo permissions. Login via SSH using syntax:

ssh -i keyFilePath/keyFile.pem ec2-user@ec2-public-dns

Switch i (-i) authenticates using the identity of the key file created in step 6.
keyFilePath is the path to the key file generated in step 6.
ec2-public-dns is the domain name for the EC2 instance retrieved in step 9.

Note: You may receive the following error when attempting to SSH to EC2.

Amazon requires the keyfile to be truly private, therefore only readable by you and no other user on the local machine. To fix the issue, change the file mode with:
chmod 700 keyFileName.pem

A successful login will present the following prompt.

Step 12  Download/Copy Node.JS.

Download Node.js to your local file system. In this example, I've downloaded Stable: 2010.11.16 node-v0.2.5.tar.gz

Open a local shell window and copy the package to the EC2 instance using secure copy.
scp -p -i ../keyPath/keyFile.pem node-v0.2.5.tar.gz ec2-user@ec2-204-236-155-210.us-west-1.compute.amazonaws.com:node.tar.gz

Step 13 Extract

The scp copy should copy node to the home/ec2-user directory. You can extract node and configure/install from this directory. The following steps assume extraction to the root opt directory so the following commands are all executed in the "Super User" sudo context to override the permission errors you'll otherwise encounter while logged in as the ec2-user user.

Copy to opt
sudo cp node.tar.gz ../../opt
cd ../../opt
sudo gunzip -d node.tar.gz
sudo tar -xf node.tar

Change to the node install directory. Listing the contents will display the following:

[ec2-user@ip-xxx-yyy-zzz node-v0.2.5]$ ll
total 112
-rw-r--r-- 1 1000 1000  4674 Nov 17 05:46 AUTHORS
drwxr-xr-x 3 1000 1000  4096 Nov 17 05:46 benchmark
drwxr-xr-x 2 1000 1000  4096 Nov 17 05:46 bin
-rw-r--r-- 1 1000 1000 31504 Nov 17 05:46 ChangeLog
-rwxr-xr-x 1 1000 1000   387 Nov 17 05:46 configure
drwxr-xr-x 7 1000 1000  4096 Nov 17 05:46 deps
drwxr-xr-x 2 1000 1000  4096 Nov 17 05:47 doc
drwxr-xr-x 2 1000 1000  4096 Nov 17 05:46 lib
-rw-r--r-- 1 1000 1000  2861 Nov 17 05:46 LICENSE
-rw-r--r-- 1 1000 1000  2218 Nov 17 05:46 Makefile
-rw-r--r-- 1 1000 1000   413 Nov 17 05:46 README
drwxr-xr-x 2 1000 1000  4096 Nov 17 05:46 src
drwxr-xr-x 8 1000 1000  4096 Nov 17 05:46 test
-rw-r--r-- 1 1000 1000  1027 Nov 17 05:46 TODO
drwxr-xr-x 5 1000 1000  4096 Nov 17 05:46 tools
-rw-r--r-- 1 1000 1000 19952 Nov 17 05:46 wscript

Step 14 Configure and Install

The next step is to configure the node environment. Typing the following will result in a dependency error
sudo ./configure

/opt/node-v0.2.5/wscript:138: error: could not configure a cxx compiler!

To fix this error, we need only to install the GCC compiler from the YUM repository hosted by Amazon

sudo yum install gcc-c++

We're not going to be installing any SSL certificates in this sandbox, so run config without support for Open SSL

sudo ./configure --without-ssl

Now you should be able to make the Node.js package

sudo make

At this point I recommend getting up and going for a walk, making a sandwich, or anything else that will kill up to 10 minutes. The small and micro instances on EC2 have limited access to CPU computing resources, making this part of the install process lengthy.

Once the make is complete, the final step is

sudo make install

You can optionally run sudo make test to verify the install.

Step 15 Hello World

Verify the installation is working by creating a file named example.js with the following contents.

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
console.log('Server running at http://ec2-204-236-155-210.us-west-1.compute.amazonaws.com:8080');

Then run node from the command line:

node example.js

Open a browser to confirm the domain is accessible from port 8080. That's it!

Because node is running as a shell process you may want to launch the server using the "no hangup" util (or 'forever') to ensure node runs beyond the shell session.

nohup node example.js &

There's huge potential for creating scalable cloud services when combining Amazon Web Services and node.js. Enjoy!

Sunday, 05 December 2010 12:05:57 (Pacific Standard Time, UTC-08:00)
Saturday, 18 December 2010 16:26:32 (Pacific Standard Time, UTC-08:00)
Thanks for the thorough documentation; saved me a lot of time and heartache.
Thursday, 23 December 2010 20:33:52 (Pacific Standard Time, UTC-08:00)
Awesome... thanks for the step-by-step. I would've been lost.
Sunday, 06 February 2011 09:06:43 (Pacific Standard Time, UTC-08:00)
Thanks a ton! Very helpful! Just in case another European user reads this: Before creating the instance you should swith to Ireland using the dropdown on the left hand side. I also found this link helpful to connect using Windows: http://clouddb.info/2009/05/17/a-quick-overview-of-putty-and-ssh-for-aws-newbies/
Tuesday, 15 February 2011 11:38:43 (Pacific Standard Time, UTC-08:00)
I'm new to AWS, new to Node.js, and new to Linux. So I would have been completely lost without your step-by-step tut. Great work, thx!
Tuesday, 15 February 2011 19:42:54 (Pacific Standard Time, UTC-08:00)
Many thanks for excellent instructions.

I got node running although I did not use your approach of downloading the tar and uploading it using scp. I just used curl as per the first example here: https://gist.github.com/579814

You are right about install/complie taking time - mine took 20 minutes!

I also assigned an elastic IP which works fine.

My question: how can I make my node app (web server) use the default port 80?

If I try this I get a Node error: "EACCES, Permission denied"

I guess I have to proxy to the port node is running on or something.

Wednesday, 16 February 2011 13:43:09 (Pacific Standard Time, UTC-08:00)
Have you tried installing npm after this? Maybe it has something to do with the ec2-user but npm always complains "npm cannot be installed without nodejs".
Wednesday, 16 February 2011 15:08:19 (Pacific Standard Time, UTC-08:00)
@ash - nothing to do with NPM, node and NPM all working fine. I'm talking about my Node app.js web server and making it listen on default web server port 80. Works fine on say 8080 but trying 80 results in the error.

Just a note for others: I ran into an isue when using Node crypto since I compiled with ssl using switch --without-ssl as mentioned above.

To compile with SSL you first need to install ssl - this can only be done as the "root" user. Switch to "root" from the ec2-user using sudo su - and then install ssl. Then switch back to ec2-user and compile Node.
Tuesday, 22 February 2011 18:21:47 (Pacific Standard Time, UTC-08:00)
Johan - You're correct. curl'ing the file would be much more efficient than scp. I too have struggled with how to run node on port 80. Unfortunately, the only solutions I've discovered involve port routing rather than directly binding the node listener to 80.
Mike Leach
Saturday, 05 March 2011 14:24:20 (Pacific Standard Time, UTC-08:00)
How were you able to get NPM to work.

I had to symbolic link /usr/local/bin/node to /usr/bin/node, so that the NPM install script could pick it up (it's run as sudo which only has /usr/bin on the $PATH).

Now I'm getting the following error:

[ec2-user@ip-10-117-55-206 ~]$ sudo sh install.sh
! [ -d .git ] || git submodule update --init
node cli.js cache clean
gmake: *** [uninstall] Segmentation fault
It failed

Saturday, 19 March 2011 13:03:02 (Pacific Standard Time, UTC-08:00)
Thanks for the post.

It doesn't seem that the AMI has make installed by default anymore. In step 14, I had to 'yum install make' before I could continue.

Also it might be easier to 'wget http://nodejs.org/dist/node-v0.4.3.tar.gz' than to download locally and scp it over.
Sunday, 20 March 2011 00:28:38 (Pacific Standard Time, UTC-08:00)
Just confirming that AMI does not seem to have 'make' installed by default anymore. Everything else is spot on. Thanks for the post. And thanks @ccorels for the heads up.
Monday, 28 March 2011 21:53:59 (Pacific Standard Time, UTC-08:00)
When I follow the steps above, I get the following error. Looks like there are issues with the current build of Node.js and the default version of GCC in Amazon.

The closest article I could find was here: http://code.google.com/p/v8/issues/detail?id=413
but, nothing recommended in those threads seems to help.

Waf: Entering directory `/opt/node-v0.4.4/build'
DEST_OS: linux
DEST_CPU: ia32
Parallel Jobs: 1
Product type: program
[51/74] libv8.a: deps/v8/SConstruct -> build/default/libv8.a
/usr/bin/python "/opt/node-v0.4.4/tools/scons/scons.py" -j 1 -C "/opt/node-v0.4.4/build/default/" -Y "/opt/node-v0.4.4/deps/v8" visibility=default mode=release arch=ia32 toolchain=gcc library=static snapshot=on
scons: Reading SConscript files ...

scons: warning: Ignoring missing SConscript 'obj/test/release/SConscript'
File "/opt/node-v0.4.4/deps/v8/SConstruct", line 1202, in BuildSpecific
scons: done reading SConscript files.
scons: Building targets ...
g++ -o obj/release/api.o -c -Wall -Werror -W -Wno-unused-parameter -Wnon-virtual-dtor -pedantic -m32 -O3 -fomit-frame-pointer -fdata-sections -ffunction-sections -ansi -fno-rtti -fno-exceptions -Wall -Werror -W -Wno-unused-parameter -Wnon-virtual-dtor -pedantic -m32 -O3 -fomit-frame-pointer -fdata-sections -ffunction-sections -ansi -DV8_TARGET_ARCH_IA32 -DENABLE_VMSTATE_TRACKING -DENABLE_LOGGING_AND_PROFILING -DENABLE_DEBUGGER_SUPPORT -I/opt/node-v0.4.4/deps/v8/src /opt/node-v0.4.4/deps/v8/src/api.cc
cc1plus: warnings being treated as errors
/opt/node-v0.4.4/deps/v8/src/handles-inl.h: In static member function 'static void v8::V8::RemoveMessageListeners(void (*)(v8::Handle<v8::Message>, v8::Handle<v8::Value>))':
/opt/node-v0.4.4/deps/v8/src/handles-inl.h:50: error: dereferencing pointer '<anonymous>' does break strict-aliasing rules
/opt/node-v0.4.4/deps/v8/src/utils.h:788: note: initialized from here
/opt/node-v0.4.4/deps/v8/src/handles-inl.h:50: error: dereferencing pointer '<anonymous>' does break strict-aliasing rules
/opt/node-v0.4.4/deps/v8/src/utils.h:788: note: initialized from here
/opt/node-v0.4.4/deps/v8/src/handles-inl.h:50: error: dereferencing pointer '<anonymous>' does break strict-aliasing rules
/opt/node-v0.4.4/deps/v8/src/utils.h:788: note: initialized from here
/opt/node-v0.4.4/deps/v8/src/handles-inl.h:50: error: dereferencing pointer '<anonymous>' does break strict-aliasing rules
/opt/node-v0.4.4/deps/v8/src/utils.h:788: note: initialized from here
cc1plus: error: dereferencing pointer 'listeners.v8::NeanderArray::obj_.v8::NeanderObject::value_.v8::internal::Handle<v8::internal::JSObject>::location_' does break strict-aliasing rules
cc1plus: error: dereferencing pointer 'listeners.v8::NeanderArray::obj_.v8::NeanderObject::value_.v8::internal::Handle<v8::internal::JSObject>::location_' does break strict-aliasing rules
cc1plus: error: dereferencing pointer 'listeners.v8::NeanderArray::obj_.v8::NeanderObject::value_.v8::internal::Handle<v8::internal::JSObject>::location_' does break strict-aliasing rules
cc1plus: error: dereferencing pointer 'listeners.v8::NeanderArray::obj_.v8::NeanderObject::value_.v8::internal::Handle<v8::internal::JSObject>::location_' does break strict-aliasing rules
/opt/node-v0.4.4/deps/v8/src/handles.h:81: note: initialized from here
scons: *** [obj/release/api.o] Error 1
scons: building terminated because of errors.
Waf: Leaving directory `/opt/node-v0.4.4/build'
Build failed: -> task failed (err #2):
{task: libv8.a SConstruct -> libv8.a}
make: *** [program] Error 1
Wednesday, 30 March 2011 21:19:38 (Pacific Standard Time, UTC-08:00)
Same issue....
Thursday, 31 March 2011 11:00:23 (Pacific Standard Time, UTC-08:00)
Same issue as well trying v0.4.4 or 0.4.3.

v0.2.5 seems to have compiled. Will test this then determine which is the latest version of node that will compile tomorrow. Anyone work through this?
Thursday, 28 April 2011 20:56:19 (Pacific Daylight Time, UTC-07:00)
Great post. Thank you fro sharing
Comments are closed.