Skip to content

Instantly share code, notes, and snippets.

@da-n
Created June 17, 2013 23:15
Show Gist options
  • Save da-n/5801350 to your computer and use it in GitHub Desktop.
Save da-n/5801350 to your computer and use it in GitHub Desktop.
Node.js, Nginx, Varnish, Upstart and Monit

Node.js, Nginx, Varnish, Upstart and Monit

This guide is based on the following article http://blog.dealspotapp.com/post/40184153657/node-js-production-deployment-with-nginx-varnish

This setup is tested deploying on Ubuntu Server.

Nginx

sudo apt-get update
sudo apt-get install nginx

Next create site config, replace myapp with desired name

sudo vim /etc/nginx/sites-available/myapp

Paste the following, adapt as needed

upstream myapp_upstream {
    server 127.0.0.1:3000;
    keepalive 64;
}

server {
    listen 8080;
    server_name myapp.com;
    return 301 $scheme://www.myapp.com$request_uri;
}

server {
    listen 8080;
    server_name www.myapp.com;
    error_page 400 404 500 502 503 504 /50x.html;
    location  /50x.html {
        internal;
        root /usr/share/nginx/www;
    }
    location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico|home/|html|xml) {
    root /home/user/myapp/public;
    access_log off;
    expires max;
    }

    location / {
        proxy_redirect off;
        proxy_set_header   X-Real-IP            $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_set_header   Host                   $http_host;
        proxy_set_header   X-NginX-Proxy    true;
        proxy_set_header   Connection "";
        proxy_http_version 1.1;
        proxy_pass         http://myapp_upstream;
        proxy_intercept_errors on;
    }
}

First we define an upstream, which tells Nginx about our Node.js servers (If you have multiple servers just add all of them).

We define a redirection from myapp.com to www.myapp.com, keeping the original request URI.

Finally, We tell Nginx to handle HTTP error codes (the internal location means that the error page isn’t publicly accessible).

Then We tell Nginx to intercept any static assets (by directory and by extensions) and serve them directly. We also set the required headers and pass traffic to the upstream server we defined earlier.

As a final step We need to create a symbolic link from the file in /etc/nginx/sites-available to /etc/nginx/sites-enabled. Make sure that there are no other enabled sites with conflicting configuration.

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp

Notice that We told Nginx to listen on port 8080

Restart Nginx:

sudo service nginx restart

Varnish

sudo apt-get install varnish

Edit /etc/default/varnish and tell Varnish to listen on port 80

DAEMON_OPTS="-a :80 \
     -T localhost:6082 \
     -f /etc/varnish/default.vcl \
     -S /etc/varnish/secret \
     -s malloc,256m"

Next edit /etc/varnish/default.vcl

# Default backend definition.  Set this to point to your content
# server.
#
backend default {
    .host = "127.0.0.1";
    .port = "8080";
}
  
  sub vcl_recv {
    if (req.restarts == 0) {
        if (req.http.x-forwarded-for) {
            set req.http.X-Forwarded-For =
                req.http.X-Forwarded-For + ", " + client.ip;
        } else {
            set req.http.X-Forwarded-For = client.ip;
        }
     }
     if (req.request != "GET" &&
       req.request != "HEAD" &&
       req.request != "PUT" &&
       req.request != "POST" &&
       req.request != "TRACE" &&
       req.request != "OPTIONS" &&
       req.request != "DELETE") {
         /* Non-RFC2616 or CONNECT which is weird. */
         return (pipe);
     }
     if (req.request != "GET" && req.request != "HEAD") {
         /* We only deal with GET and HEAD by default */
         return (pass);
     }
     # force lookup for static assets
     if (req.url ~ "\.(png|gif|jpg|swf|css|js|html|ico)$") {
         return(lookup);
     }
     if (req.http.Authorization || req.http.Cookie) {
         /* Not cacheable by default */
         return (pass);
     }
     return (lookup);
 }
 
  sub vcl_fetch {
     # strip the cookie before static asset is inserted into cache.
     if (req.url ~ "\.(png|gif|jpg|swf|css|js|html|ico)$") {
         unset beresp.http.set-cookie;
     }
     if (beresp.ttl <= 0s ||
         beresp.http.Set-Cookie ||
         beresp.http.Vary == "*") {
                /*
                 * Mark as "Hit-For-Pass" for the next 2 minutes
                 */
                set beresp.ttl = 120 s;
                return (hit_for_pass);
     }
     return (deliver);
  }

What I’m doing here is telling Varnish about the Nginx server, in sub_recv making sure that static assets are always fetched from cache and in vcl_fetch stripping any cookies before static assets are inserted into cache (Varnish doesn't cache requests with cookies by default).

restart Varnish

sudo service varnish restart

Upstart

Create a new file under /etc/init/myapp.conf

#/etc/init/myapp.conf
description "MyApp Node.js"
author "xxx@myapp.com"
 
start on (local-filesystems and net-device-up IFACE=eth0)
stop on shutdown
 
respawn
respawn limit 5 60
 
script
        cd /home/user/myapp
        exec sudo -u user NODE_ENV=production /usr/bin/node /home/user/myapp/server.js >> /home/user/myapp/log/server.log 2>&1
end script

Here We tell Upstart how to start our process, to respawn it if it dies and to auto-start on boot after the file systems and network interface are up.

Now We can start and stop our Node.js server

sudo service myapp start
sudo service myapp stop
sudo service myapp restart

Monit

sudo apt-get install monit

Edit /etc/monit/monitrc and uncomment the lines with set httpd.. use address localhost and allow localhost, We need those in order to use monit status.

Create a new file under /etc/monit/conf.d/myapp.conf

    check host localhost with address 127.0.0.1
            start "/sbin/start myapp"
            stop "/sbin/stop myapp"
            if failed port 3000 protocol HTTP
                    request /
                    with timeout 5 seconds
                    then restart

This tells Monit how to start and stop the Node.js server, How to check for a response and how often.

Restart Monit:

sudo service monit restart

Now everything should be working, the Node.js server is started on boot with Upstart and monitored with Monit. HTTP requests are handled with Nginx and cached with Varnish.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment