The Node Den
Setting Up a Node.js Website
What if you just want to get started hosting a basic web site using Node? Node.js makes for a fine basic web server that is easy to set up and offers you the ability to get your feet wet with Node.js while giving you a place to start expanding functionality using Node.
There are a few common challenges to overcome first such as:
- Installing Node.js
- Using Node as a static file web server
- Running a Node app as a service so it starts on system boot and restarts after any potential crashes
- Hosting more than one Node app site on the same server
This article assumes you are using the Ubuntu Linux platform such as I discuss in Starting Linux for Windows developers.
I. Install Node.js
ssh into your server as a user that has root sudo privileges. So using our example from before:
ssh myadmin@162.243.238.83 -p 50231
Now run these commands one at a time to install the latest version of Node.js:
sudo apt-get update
sudo apt-get install python-software-properties python g++ make
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs
Notice that the first command is apt-get update
. It is always a good idea to make sure your Ubuntu package manager has the latest package settings before starting any install.
To verify Node has been installed:
which node
node -v
This will tell you where Node is running from, and which version you are running.
II. Set up a Node.js static file server
In preparation for setting up a Git deployment process later, let's create a new Linux user called git
who will own the web site files:
sudo adduser git
Follow the prompts and set the user's name to something like "Git user" - you can leave the rest of the settings blank. Make sure you make a note somewhere of the password you use for this new user!
Now change to this new git user using: su git
. Use the password you just created above.
Create a new directory where the node server app and web site files will be located:
cd /home/git
mkdir mysite
cd mysite
mkdir public
Note that is is fairly standard to use public as the web root directory in node web sites - much as you're used to seeing wwwroot in IIS.
We're going to use connect to create our web server. Connect is a Node.js module that acts as a router for incoming requests to the server and offers a number of very popular "middleware" modules to handle many standard functions.
The most popular Node.js server framework module is probably Express, which is built on connect. I opt to use connect without Express because I don't leverage the templating and server state management functionality of Express. If you use Express, there are just some slight syntax changes to what you will see here when using stand-alone connect.
To install connect for this web site, make sure you are in the /home/git/mysite
directory and use the Node.js NPM package manager to install connect as follows:
npm install connect
This should load the connect files and finish showing you the structure something like this:
connect@2.12.0 node_modules/connect
├── uid2@0.0.3
├── methods@0.1.0
├── cookie-signature@1.0.1
├── pause@0.0.1
├── fresh@0.2.0
├── qs@0.6.6
├── debug@0.7.4
├── bytes@0.2.1
├── buffer-crc32@0.2.1
├── raw-body@1.1.2
├── batch@0.5.0
├── cookie@0.1.0
├── negotiator@0.3.0
├── send@0.1.4 (range-parser@0.0.4, mime@1.2.11)
└── multiparty@2.2.0 (stream-counter@0.2.0, readable-stream@1.1.9)
Now you have a node_modules
directory under your mysite
directory. This is the standard directory other node modules will go in as well as you install them with NPM. You can also put your custom modules there.
Now create the actual node server app. Make sure you are still in /home/git/mysite
and run the command cat > server.js
and paste the following code in:
var http = require("http");
var connect = require('connect');
console.log('\n\n--- Node Version: ' + process.version + ' ---');
// Set up Connect routing
var app = connect()
.use(connect.static(__dirname + '/public'))
.use(function(req, res) {
console.log('Could not find handler for: ' + req.url);
res.end('Could not find handler for: ' + req.url);
})
.use(function(err, req, res, next) {
console.log('Error trapped by Connect: ' + err.message + ' : ' + err.stack);
res.end('Error trapped by Connect: ' + err.message);
});
// Start node server listening on specified port -----
http.createServer(app).listen(80);
console.log('HTTP server listening on port 80');
Remember to use ctrl-d
to finish the cat file creation process.
Now create the good old fashioned "Hello World" HTML file in the public folder: cd public
and cat > index.html
, then paste in the html:
<html>
<head>
<title>Mysite</title>
</head>
<body>
Hello World!
</body>
</html>
Now to test your site. At this point, you can only run the node app when you have root privileges, because it is using port 80, which is a privileged port. We will fix this later - but for now, change to root using: su -
. (Or you can use your administrative account by putting sudo
in front of the node command.)
Now cd /home/git/myite
and use this command to start the node server app:
node server.js
If all went well, the server will be running and outputting any standard output (when you use console.log()
in the app) to the terminal. Now use your web browser to see if it worked. Go to http://your.ip.address
and you should see the "Hello World!" text. If you get any errors, you will see them in the server terminal window.
Notice that connect's static middleware defaults to use index.html as a default file of no resource was specified. You may also see
Could not find handler for: /favicon.ico
logged to the screen - that's ok for now - your browser was just looking for the favicon file which you do not have yet.
To stop the server, use ctrl-c
in the terminal.
III. Run your Node app as a service
Ubuntu has a nice process for creating system services called Upstart. You create a simple script to define your service which will allow you to:
- Start the Node app whenever the server restarts
- Restart the app if it should fail for some reason (a crash from a bug, memory leak, etc.)
- Conveniently start/stop the app via system commands
To create the script you must be root user. Change to root and create the Upstart script as follows. The name of your .conf file will become the name of your new service:
su -
cd /etc/init
cat > mysite.conf
Now paste in the script:
description "Mysite Node Service"
author "Your info ifya want"
start on started mountall
stop on shutdown
respawn
respawn limit 99 5
script
sudo node /home/git/mysite/server.js >> /var/log/mysite.log 2>&1
end script
post-start script
end script
This service script tells the server to execute your node app, and log the stdout and stderr from the app to the log file you specified. Once started, it will keep running now. Note: if you have a major error in the app that causes a crash on startup, this script will start it over and over again 99 times (or keep trying to start it for 5 seconds, whichever comes first) before giving up.
(Note the use of sudo
to execute the node server. This will help later when we grant the git user sudo permissions to start/stop the app.)
Now you can start your node website service using:
start mysite
It should start and tell you the process id, something like:
mysite start/running, process 7412
Now test the site with your browser - it should be working. Now at this point, if you reboot the server, the mysite service will start automatically! To stop or restart your service, use stop mysite
and restart mysite
. Nice!
IV. Using Nginx to host multiple node sites
At this point you can put any html, css, JavaScript, and image files in your public directory and host them using your node server. But what if you want to run several node web sites on the same machine? Assuming you have domain names for each site and can point their DNS records to your server's IP address, you can use the following solution.
One common approach is to use Nginx as a reverse proxy. In this scenario, Nginx receives all public requests for your websites and routes the requests to your Node apps based on the requested host and domain name, and then sends your Node app's responses back to the client.
Nginx is very fast, and easy to configure - making it a great solution for this purpose.
Install Nginx- First make sure your Node app is not running to avoid any conflicts with port 80: sudo stop mysite
. Remember that you have to be root or in the sudo group to stop your service at this point.
Now install Nginx:
sudo apt-get update
sudo apt-get install nginx
sudo service nginx start
Test Nginx is running by pointing your browser to your server: http://your.ip.address
. You should see the Nginx splash screen at this point.
Update your Node app port- For this approach, each of your Node apps will run on a different port, and Nginx will use that port to find them. So update your server.js file to change the port by editing the last few lines to look like this to move the app to port 8000:
// Start node server listening on specified port -----
http.createServer(app).listen(8000);
console.log('HTTP server listening on port 8000');
Configure Nginx- For each of the Node sites you want to host, create an Nginx .conf file. You can use any name for the .conf file, but matching your site name will help avoid confusion. Use root user for this:
su -
cd /etc/nginx/conf.d
cat > mysite.conf
Now paste in this file content, modified for your setup:
server {
listen 80;
server_name mysite.com www.mysite.com;
location / {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
The important settings are the server_name
and proxy_pass
lines.
This file tells Nginx to listen for any requests on port 80 for mysite.com or www.mysite.com and route them to http://localhost:8000
which is the port our Node app is listening on. (You will assign different unique port numbers for each subsequent Node app you want to host.)
Note: You can also combine all your sites into one .conf file - just put one server {} block after another. Also, you can use *.yourdomain.com to grab any host names and route them. I did have one issue where I needed to specifically add mydomain.com *.mydomain.com
for it to route requests to mydomain.com with no host prefix.
To test the Nginx reverse proxy process:
- Restart Nginx to enable the configuration:
service nginx restart
(or usenginx -s reload
) node /home/git/mysite/server.js
to start the Node app- Point your web browser to yoursite.com and verify you see your site now
If you map more than a few domain names, the proxy may fail and you will get something like the following error in the Nginx error log:
could not build the server_names_hash, you should increase server_names_hash_bucket_size: 32
To increase the server_names_hash_bucket_size
, edit the nginx.conf file using vi:
vi /etc/nginx/nginx.conf
The following line may be commented out with a #
at the front of the line. Delete the #
to re-activate the line:
server_names_hash_bucket_size 64;
Restart Nginx. If 64 does not fix the problem at first, increase it by a powers of 2, until it works (64, 128, 256, etc.)
Troubleshooting: To better see what is going on, you can monitor the Nginx error or access logs while you try to reach the server:
tail /var/log/nginx/access.log -f
tail /var/log/nginx/error.log -f
tail
will show you the last few lines of the log, and the -f
will let you monitor any new log entries as they come in. Use ctrl-c
to exit.
In the next article we'll set up an efficient Git deployment process so you can easily develop on your laptop and push full site deployments to your hosted server with one command.
For a full introduction and index to this blog: Node.js: One New Approach
Next post I talk about Using Git for deployment.
Cheers!