diff --git a/Makefile b/Makefile
index 8dfb505424d34053a5ca34f72741477eafec89b5..f7539216dad2303da7adb4f146ec77d2e5b5d5e8 100644
--- a/Makefile
+++ b/Makefile
@@ -226,26 +226,44 @@ DEFINES_mutrace = _DEBUG DEBUG
 
 prefix ?= /usr/local
 
-PROTOC = protoc
-DTRACE = dtrace
+PROTOC ?= protoc
+DTRACE ?= dtrace
 CONFIG ?= opt
+# Doing X ?= Y is the same as:
+# ifeq ($(origin X), undefined)
+#  X = Y
+# endif
+# but some variables, such as CC, CXX, LD or AR, have defaults.
+# So instead of using ?= on them, we need to check their origin.
+# See:
+#  https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html
+#  https://www.gnu.org/software/make/manual/html_node/Flavors.html#index-_003f_003d
+#  https://www.gnu.org/software/make/manual/html_node/Origin-Function.html
+ifeq ($(origin CC), default)
 CC = $(CC_$(CONFIG))
+endif
+ifeq ($(origin CXX), default)
 CXX = $(CXX_$(CONFIG))
+endif
+ifeq ($(origin LD), default)
 LD = $(LD_$(CONFIG))
-LDXX = $(LDXX_$(CONFIG))
+endif
+LDXX ?= $(LDXX_$(CONFIG))
+ifeq ($(origin AR), default)
 AR = ar
+endif
 ifeq ($(SYSTEM),Linux)
-STRIP = strip --strip-unneeded
+STRIP ?= strip --strip-unneeded
 else
 ifeq ($(SYSTEM),Darwin)
-STRIP = strip -x
+STRIP ?= strip -x
 else
-STRIP = strip
+STRIP ?= strip
 endif
 endif
-INSTALL = install
-RM = rm -f
-PKG_CONFIG = pkg-config
+INSTALL ?= install
+RM ?= rm -f
+PKG_CONFIG ?= pkg-config
 
 ifndef VALID_CONFIG_$(CONFIG)
 $(error Invalid CONFIG value '$(CONFIG)')
@@ -261,15 +279,21 @@ endif
 CXX11_CHECK_CMD = $(CXX) -std=c++11 -o $(TMPOUT) -c test/build/c++11.cc
 HAS_CXX11 = $(shell $(CXX11_CHECK_CMD) 2> /dev/null && echo true || echo false)
 
+CHECK_NO_SHIFT_NEGATIVE_VALUE_CMD = $(CC) -std=c99 -Werror -Wno-shift-negative-value -o $(TMPOUT) -c test/build/empty.c
+HAS_NO_SHIFT_NEGATIVE_VALUE = $(shell $(CHECK_NO_SHIFT_NEGATIVE_VALUE_CMD) 2> /dev/null && echo true || echo false)
+ifeq ($(HAS_NO_SHIFT_NEGATIVE_VALUE),true)
+W_NO_SHIFT_NEGATIVE_VALUE=-Wno-shift-negative-value
+endif
+
 # The HOST compiler settings are used to compile the protoc plugins.
 # In most cases, you won't have to change anything, but if you are
 # cross-compiling, you can override these variables from GNU make's
 # command line: make CC=cross-gcc HOST_CC=gcc
 
-HOST_CC = $(CC)
-HOST_CXX = $(CXX)
-HOST_LD = $(LD)
-HOST_LDXX = $(LDXX)
+HOST_CC ?= $(CC)
+HOST_CXX ?= $(CXX)
+HOST_LD ?= $(LD)
+HOST_LDXX ?= $(LDXX)
 
 ifdef EXTRA_DEFINES
 DEFINES += $(EXTRA_DEFINES)
@@ -400,14 +424,6 @@ else
 IS_GIT_FOLDER = true
 endif
 
-ifeq ($(SYSTEM),Linux)
-OPENSSL_REQUIRES_DL = true
-endif
-
-ifeq ($(SYSTEM),Darwin)
-OPENSSL_REQUIRES_DL = true
-endif
-
 ifeq ($(HAS_PKG_CONFIG),true)
 OPENSSL_ALPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.2 openssl
 OPENSSL_NPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.1 openssl
@@ -426,11 +442,6 @@ OPENSSL_NPN_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/open
 ZLIB_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/zlib.c -lz $(LDFLAGS)
 PROTOBUF_CHECK_CMD = $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/protobuf.cc -lprotobuf $(LDFLAGS)
 
-ifeq ($(OPENSSL_REQUIRES_DL),true)
-OPENSSL_ALPN_CHECK_CMD += -ldl
-OPENSSL_NPN_CHECK_CMD += -ldl
-endif
-
 endif # HAS_PKG_CONFIG
 
 PERFTOOLS_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/perftools.c -lprofiler $(LDFLAGS)
@@ -594,9 +605,6 @@ OPENSSL_DEP += $(LIBDIR)/$(CONFIG)/libboringssl.a
 OPENSSL_MERGE_LIBS += $(LIBDIR)/$(CONFIG)/libboringssl.a
 # need to prefix these to ensure overriding system libraries
 CPPFLAGS := -Ithird_party/boringssl/include $(CPPFLAGS)
-ifeq ($(OPENSSL_REQUIRES_DL),true)
-LIBS_SECURE = dl
-endif # OPENSSL_REQUIRES_DL
 else # EMBED_OPENSSL=false
 ifeq ($(HAS_PKG_CONFIG),true)
 OPENSSL_PKG_CONFIG = true
@@ -616,10 +624,7 @@ ifeq ($(HAS_SYSTEM_OPENSSL_NPN),true)
 CPPFLAGS += -DTSI_OPENSSL_ALPN_SUPPORT=0
 LIBS_SECURE = $(OPENSSL_LIBS)
 endif # HAS_SYSTEM_OPENSSL_NPN
-ifeq ($(OPENSSL_REQUIRES_DL),true)
-LIBS_SECURE += dl
 PC_LIBS_SECURE = $(addprefix -l, $(LIBS_SECURE))
-endif # OPENSSL_REQUIRES_DL=true
 endif # EMBED_OPENSSL
 endif # NO_SECURE
 
@@ -5486,7 +5491,7 @@ LIBZ_SRC = \
 
 LIBZ_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(LIBZ_SRC))))
 
-$(LIBZ_OBJS): CFLAGS := $(CFLAGS) -Wno-sign-conversion -Wno-conversion -Wno-unused-value -Wno-implicit-function-declaration -fvisibility=hidden
+$(LIBZ_OBJS): CFLAGS := $(CFLAGS) -Wno-sign-conversion -Wno-conversion -Wno-unused-value -Wno-implicit-function-declaration $(W_NO_SHIFT_NEGATIVE_VALUE) -fvisibility=hidden
 
 $(LIBDIR)/$(CONFIG)/libz.a: $(ZLIB_DEP) $(OPENSSL_DEP)  $(LIBZ_OBJS)
 	$(E) "[AR]      Creating $@"
diff --git a/binding.gyp b/binding.gyp
index 26d090743b5fa9a66ffbe6b7ae8b83cc3f38e91a..b1c0214d5fb82b1a1e194f8ac052f89613f7801d 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -5,7 +5,7 @@
 # This file can be regenerated from the template by running
 # tools/buildgen/generate_projects.sh
 
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -723,5 +723,16 @@
         "gpr",
       ]
     },
+    {
+      "target_name": "action_after_build",
+      "type": "none",
+      "dependencies": [ "<(module_name)" ],
+      "copies": [
+        {
+          "files": [ "<(PRODUCT_DIR)/<(module_name).node"],
+          "destination": "<(module_path)"
+        }
+      ]
+    }
   ]
 }
diff --git a/package.json b/package.json
index d0f3d1bf105ccdf63cd440ff2e9402d8cc2d67be..2abc266d11626047589774dd224813222dca63fa 100644
--- a/package.json
+++ b/package.json
@@ -22,12 +22,14 @@
     "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js",
     "test": "./node_modules/.bin/mocha src/node/test && npm run-script lint",
     "gen_docs": "./node_modules/.bin/jsdoc -c src/node/jsdoc_conf.json",
-    "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test"
+    "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test",
+    "preinstall": "npm install node-pre-gyp",
+    "install": "./node_modules/.bin/node-pre-gyp install --fallback-to-build"
   },
   "dependencies": {
-    "bindings": "^1.2.0",
     "lodash": "^3.9.3",
     "nan": "^2.0.0",
+    "node-pre-gyp": "^0.6.19",
     "protobufjs": "^4.0.0"
   },
   "devDependencies": {
@@ -45,6 +47,14 @@
   "engines": {
     "node": ">=0.10.13"
   },
+  "binary": {
+    "module_name": "grpc_node",
+    "module_path": "./build/Release/",
+    "host": "https://storage.googleapis.com/",
+    "remote_path": "grpc-precompiled-binaries/node/{name}/v{version}",
+    "package_name": "{node_abi}-{platform}-{arch}.tar.gz",
+    "module_path": "src/node/extension_binary"
+  },
   "files": [
     "LICENSE",
     "src/node/README.md",
diff --git a/src/core/census/initialize.c b/src/core/census/initialize.c
index b7af714e0bebdfdce0144badf9f0edf1396c3394..ce7ec09b89fe663d92ab946b0169bbf4abbac060 100644
--- a/src/core/census/initialize.c
+++ b/src/core/census/initialize.c
@@ -37,9 +37,7 @@ static int features_enabled = CENSUS_FEATURE_NONE;
 
 int census_initialize(int features) {
   if (features_enabled != CENSUS_FEATURE_NONE) {
-    return 1;
-  }
-  if (features == CENSUS_FEATURE_NONE) {
+    // Must have been a previous call to census_initialize; return error
     return 1;
   }
   features_enabled = features;
diff --git a/src/node/ext/byte_buffer.cc b/src/node/ext/byte_buffer.cc
index c306292c04c89496b6912c148c8c2e11e06d7d0a..ee703fdc917a1ce383a6357b33b5b58b36082073 100644
--- a/src/node/ext/byte_buffer.cc
+++ b/src/node/ext/byte_buffer.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -69,7 +69,7 @@ Local<Value> ByteBufferToBuffer(grpc_byte_buffer *buffer) {
     return scope.Escape(Nan::Null());
   }
   size_t length = grpc_byte_buffer_length(buffer);
-  char *result = reinterpret_cast<char *>(calloc(length, sizeof(char)));
+  char *result = new char[length];
   size_t offset = 0;
   grpc_byte_buffer_reader reader;
   grpc_byte_buffer_reader_init(&reader, buffer);
diff --git a/src/node/index.js b/src/node/index.js
index 0d1a7fd887bd5724eee705d2fa919c85c3c27f4f..7eacdc67b1d6ae0b460f8e747ac8f4d879a5aeac 100644
--- a/src/node/index.js
+++ b/src/node/index.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -51,7 +51,7 @@ var server = require('./src/server.js');
 
 var Metadata = require('./src/metadata.js');
 
-var grpc = require('bindings')('grpc_node');
+var grpc = require('./src/grpc_extension');
 
 /**
  * Load a gRPC object from an existing ProtoBuf.Reflect object.
diff --git a/src/node/src/client.js b/src/node/src/client.js
index d57826781d391d6872e6f00b757ee380ffa8a134..b5247a69ee01610313aab95dda1df1dbffa07283 100644
--- a/src/node/src/client.js
+++ b/src/node/src/client.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -51,7 +51,7 @@
 
 var _ = require('lodash');
 
-var grpc = require('bindings')('grpc_node');
+var grpc = require('./grpc_extension');
 
 var common = require('./common');
 
diff --git a/src/node/src/credentials.js b/src/node/src/credentials.js
index dcbfac18f4647900db5fd9496663d133ecd5f3ba..710ab6d879cd36c8e13da6523de5d888919699a2 100644
--- a/src/node/src/credentials.js
+++ b/src/node/src/credentials.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -61,7 +61,7 @@
 
 'use strict';
 
-var grpc = require('bindings')('grpc_node.node');
+var grpc = require('./grpc_extension');
 
 var CallCredentials = grpc.CallCredentials;
 
diff --git a/src/node/src/grpc_extension.js b/src/node/src/grpc_extension.js
new file mode 100644
index 0000000000000000000000000000000000000000..d4eca65fcbf422595644820d7b8ba00d51c986eb
--- /dev/null
+++ b/src/node/src/grpc_extension.js
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+var binary = require('node-pre-gyp');
+var path = require('path');
+var binding_path = binary.find(path.resolve(
+    path.join(__dirname,'../../../package.json')));
+var binding = require(binding_path);
+
+module.exports = binding;
diff --git a/src/node/src/metadata.js b/src/node/src/metadata.js
index fef79f959e49323e6e5fad04f106324f21678c37..51a9f8a21622d71296a8000efcc6f7eefc772ed1 100644
--- a/src/node/src/metadata.js
+++ b/src/node/src/metadata.js
@@ -49,7 +49,7 @@
 
 var _ = require('lodash');
 
-var grpc = require('bindings')('grpc_node');
+var grpc = require('./grpc_extension');
 
 /**
  * Class for storing metadata. Keys are normalized to lowercase ASCII.
diff --git a/src/node/src/server.js b/src/node/src/server.js
index ceaa9f5d1fcbfe840653f98ef1d9599aeff03555..e5aadcd5658e05652f14c4045ad5faa9abe66af6 100644
--- a/src/node/src/server.js
+++ b/src/node/src/server.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -51,7 +51,7 @@
 
 var _ = require('lodash');
 
-var grpc = require('bindings')('grpc_node');
+var grpc = require('./grpc_extension');
 
 var common = require('./common');
 
diff --git a/src/node/test/call_test.js b/src/node/test/call_test.js
index f1f86b35db39a4efc201e415dc26e73326917558..2300096d03e272910c0279ce3aca826af0288709 100644
--- a/src/node/test/call_test.js
+++ b/src/node/test/call_test.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,7 +34,7 @@
 'use strict';
 
 var assert = require('assert');
-var grpc = require('bindings')('grpc_node');
+var grpc = require('../src/grpc_extension');
 
 /**
  * Helper function to return an absolute deadline given a relative timeout in
diff --git a/src/node/test/channel_test.js b/src/node/test/channel_test.js
index 7163a5fb5ee2467ae025a43f6da5623c718d22ff..c0ae2b769a0ea99d3128aed3c011ea644219b398 100644
--- a/src/node/test/channel_test.js
+++ b/src/node/test/channel_test.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,7 +34,7 @@
 'use strict';
 
 var assert = require('assert');
-var grpc = require('bindings')('grpc_node');
+var grpc = require('../src/grpc_extension');
 
 /**
  * This is used for testing functions with multiple asynchronous calls that
diff --git a/src/node/test/constant_test.js b/src/node/test/constant_test.js
index b17cd339cb0effafdacfd574606376e8f73646f5..712c70706d95b962635bc99086fdd00882c608db 100644
--- a/src/node/test/constant_test.js
+++ b/src/node/test/constant_test.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,7 +34,7 @@
 'use strict';
 
 var assert = require('assert');
-var grpc = require('bindings')('grpc_node');
+var grpc = require('../src/grpc_extension');
 
 /**
  * List of all status names
diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js
index 0f6c5941c4de48246510019d009851d9838ff305..353c6c761de3564c4e7a80c4cdc2e0e504dcae22 100644
--- a/src/node/test/end_to_end_test.js
+++ b/src/node/test/end_to_end_test.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -34,7 +34,7 @@
 'use strict';
 
 var assert = require('assert');
-var grpc = require('bindings')('grpc_node');
+var grpc = require('../src/grpc_extension');
 
 /**
  * This is used for testing functions with multiple asynchronous calls that
diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js
index 592f47e1456db6d4f99e34668b89c2440ae034ec..71a964718443bf21b32a043382fafa784801f7fe 100644
--- a/src/node/test/server_test.js
+++ b/src/node/test/server_test.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -36,7 +36,7 @@
 var assert = require('assert');
 var fs = require('fs');
 var path = require('path');
-var grpc = require('bindings')('grpc_node');
+var grpc = require('../src/grpc_extension');
 
 describe('server', function() {
   describe('constructor', function() {
diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js
index fc765ed7314ff8b1239a56ca49d9e3c8ab19148a..c65d95f76730f6cc47be74fb816c878cd4a95764 100644
--- a/src/node/test/surface_test.js
+++ b/src/node/test/surface_test.js
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -952,6 +952,7 @@ describe('Call propagation', function() {
   describe('Cancellation', function() {
     it('With a unary call', function(done) {
       done = multiDone(done, 2);
+      var call;
       proxy_impl.unary = function(parent, callback) {
         client.unary(parent.request, function(err, value) {
           try {
@@ -969,12 +970,13 @@ describe('Call propagation', function() {
       proxy.start();
       var proxy_client = new Client('localhost:' + proxy_port,
                                     grpc.credentials.createInsecure());
-      var call = proxy_client.unary({}, function(err, value) {
+      call = proxy_client.unary({}, function(err, value) {
         done();
       });
     });
     it('With a client stream call', function(done) {
       done = multiDone(done, 2);
+      var call;
       proxy_impl.clientStream = function(parent, callback) {
         client.clientStream(function(err, value) {
           try {
@@ -992,12 +994,13 @@ describe('Call propagation', function() {
       proxy.start();
       var proxy_client = new Client('localhost:' + proxy_port,
                                     grpc.credentials.createInsecure());
-      var call = proxy_client.clientStream(function(err, value) {
+      call = proxy_client.clientStream(function(err, value) {
         done();
       });
     });
     it('With a server stream call', function(done) {
       done = multiDone(done, 2);
+      var call;
       proxy_impl.serverStream = function(parent) {
         var child = client.serverStream(parent.request, null,
                                         {parent: parent});
@@ -1013,13 +1016,14 @@ describe('Call propagation', function() {
       proxy.start();
       var proxy_client = new Client('localhost:' + proxy_port,
                                     grpc.credentials.createInsecure());
-      var call = proxy_client.serverStream({});
+      call = proxy_client.serverStream({});
       call.on('error', function(err) {
         done();
       });
     });
     it('With a bidi stream call', function(done) {
       done = multiDone(done, 2);
+      var call;
       proxy_impl.bidiStream = function(parent) {
         var child = client.bidiStream(null, {parent: parent});
         child.on('error', function(err) {
@@ -1034,7 +1038,7 @@ describe('Call propagation', function() {
       proxy.start();
       var proxy_client = new Client('localhost:' + proxy_port,
                                     grpc.credentials.createInsecure());
-      var call = proxy_client.bidiStream();
+      call = proxy_client.bidiStream();
       call.on('error', function(err) {
         done();
       });
diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c
index 43adafb73f9a9a0d7b81e2a99fc73c726c4424f7..8f33d72417aebfe6fa2e0e6ec9b3640828e5365a 100644
--- a/src/ruby/ext/grpc/rb_call.c
+++ b/src/ruby/ext/grpc/rb_call.c
@@ -525,12 +525,12 @@ typedef struct run_batch_stack {
   grpc_status_code recv_status;
   char *recv_status_details;
   size_t recv_status_details_capacity;
-  uint write_flag;
+  unsigned write_flag;
 } run_batch_stack;
 
 /* grpc_run_batch_stack_init ensures the run_batch_stack is properly
  * initialized */
-static void grpc_run_batch_stack_init(run_batch_stack *st, uint write_flag) {
+static void grpc_run_batch_stack_init(run_batch_stack *st, unsigned write_flag) {
   MEMZERO(st, run_batch_stack, 1);
   grpc_metadata_array_init(&st->send_metadata);
   grpc_metadata_array_init(&st->send_trailing_metadata);
@@ -696,7 +696,7 @@ static VALUE grpc_rb_call_run_batch(VALUE self, VALUE cqueue, VALUE tag,
   grpc_call_error err;
   VALUE result = Qnil;
   VALUE rb_write_flag = rb_ivar_get(self, id_write_flag);
-  uint write_flag = 0;
+  unsigned write_flag = 0;
   TypedData_Get_Struct(self, grpc_call, &grpc_call_data_type, call);
 
   /* Validate the ops args, adding them to a ruby array */
diff --git a/src/ruby/ext/grpc/rb_call_credentials.c b/src/ruby/ext/grpc/rb_call_credentials.c
index 4d719d7541754a9c3c1d7a84ba523a1084748ba7..ebcc6592c2d96cb48e1af226f03778f693908831 100644
--- a/src/ruby/ext/grpc/rb_call_credentials.c
+++ b/src/ruby/ext/grpc/rb_call_credentials.c
@@ -79,6 +79,7 @@ static VALUE grpc_rb_call_credentials_callback(VALUE callback_args) {
 static VALUE grpc_rb_call_credentials_callback_rescue(VALUE args,
                                                       VALUE exception_object) {
   VALUE result = rb_hash_new();
+  (void)args;
   rb_hash_aset(result, rb_str_new2("metadata"), Qnil);
   /* Currently only gives the exception class name. It should be possible get
      more details */
@@ -132,6 +133,7 @@ static void grpc_rb_call_credentials_plugin_get_metadata(
 }
 
 static void grpc_rb_call_credentials_plugin_destroy(void *state) {
+  (void)state;
   // Not sure what needs to be done here
 }
 
diff --git a/src/ruby/ext/grpc/rb_event_thread.c b/src/ruby/ext/grpc/rb_event_thread.c
index 95af091317ce6db0316ae6f29d4f657d4bbc61fe..516f0bdad2ce143e8e3b2fb1e2d0febcb39930b1 100644
--- a/src/ruby/ext/grpc/rb_event_thread.c
+++ b/src/ruby/ext/grpc/rb_event_thread.c
@@ -102,6 +102,7 @@ static void grpc_rb_event_queue_destroy() {
 
 static void *grpc_rb_wait_for_event_no_gil(void *param) {
   grpc_rb_event *event = NULL;
+  (void)param;
   gpr_mu_lock(&event_queue.mu);
   while ((event = grpc_rb_event_queue_dequeue()) == NULL) {
     gpr_cv_wait(&event_queue.cv,
@@ -117,6 +118,7 @@ static void *grpc_rb_wait_for_event_no_gil(void *param) {
 }
 
 static void grpc_rb_event_unblocking_func(void *arg) {
+  (void)arg;
   gpr_mu_lock(&event_queue.mu);
   event_queue.abort = true;
   gpr_cv_signal(&event_queue.cv);
@@ -127,6 +129,7 @@ static void grpc_rb_event_unblocking_func(void *arg) {
  * events */
 static VALUE grpc_rb_event_thread(VALUE arg) {
   grpc_rb_event *event;
+  (void)arg;
   while(true) {
     event = (grpc_rb_event*)rb_thread_call_without_gvl(
         grpc_rb_wait_for_event_no_gil, NULL,
diff --git a/templates/Makefile.template b/templates/Makefile.template
index 589f724fd1cd329b2be01e267cf88edd5603d6a3..57a08151937863c442d4c649b3bff6995900f43d 100644
--- a/templates/Makefile.template
+++ b/templates/Makefile.template
@@ -130,26 +130,44 @@
 
   prefix ?= /usr/local
 
-  PROTOC = protoc
-  DTRACE = dtrace
+  PROTOC ?= protoc
+  DTRACE ?= dtrace
   CONFIG ?= opt
+  # Doing X ?= Y is the same as:
+  # ifeq ($(origin X), undefined)
+  #  X = Y
+  # endif
+  # but some variables, such as CC, CXX, LD or AR, have defaults.
+  # So instead of using ?= on them, we need to check their origin.
+  # See:
+  #  https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html
+  #  https://www.gnu.org/software/make/manual/html_node/Flavors.html#index-_003f_003d
+  #  https://www.gnu.org/software/make/manual/html_node/Origin-Function.html
+  ifeq ($(origin CC), default)
   CC = $(CC_$(CONFIG))
+  endif
+  ifeq ($(origin CXX), default)
   CXX = $(CXX_$(CONFIG))
+  endif
+  ifeq ($(origin LD), default)
   LD = $(LD_$(CONFIG))
-  LDXX = $(LDXX_$(CONFIG))
+  endif
+  LDXX ?= $(LDXX_$(CONFIG))
+  ifeq ($(origin AR), default)
   AR = ar
+  endif
   ifeq ($(SYSTEM),Linux)
-  STRIP = strip --strip-unneeded
+  STRIP ?= strip --strip-unneeded
   else
   ifeq ($(SYSTEM),Darwin)
-  STRIP = strip -x
+  STRIP ?= strip -x
   else
-  STRIP = strip
+  STRIP ?= strip
   endif
   endif
-  INSTALL = install
-  RM = rm -f
-  PKG_CONFIG = pkg-config
+  INSTALL ?= install
+  RM ?= rm -f
+  PKG_CONFIG ?= pkg-config
 
   ifndef VALID_CONFIG_$(CONFIG)
   $(error Invalid CONFIG value '$(CONFIG)')
@@ -165,15 +183,21 @@
   CXX11_CHECK_CMD = $(CXX) -std=c++11 -o $(TMPOUT) -c test/build/c++11.cc
   HAS_CXX11 = $(shell $(CXX11_CHECK_CMD) 2> /dev/null && echo true || echo false)
 
+  CHECK_NO_SHIFT_NEGATIVE_VALUE_CMD = $(CC) -std=c99 -Werror -Wno-shift-negative-value -o $(TMPOUT) -c test/build/empty.c
+  HAS_NO_SHIFT_NEGATIVE_VALUE = $(shell $(CHECK_NO_SHIFT_NEGATIVE_VALUE_CMD) 2> /dev/null && echo true || echo false)
+  ifeq ($(HAS_NO_SHIFT_NEGATIVE_VALUE),true)
+  W_NO_SHIFT_NEGATIVE_VALUE=-Wno-shift-negative-value
+  endif
+
   # The HOST compiler settings are used to compile the protoc plugins.
   # In most cases, you won't have to change anything, but if you are
   # cross-compiling, you can override these variables from GNU make's
   # command line: make CC=cross-gcc HOST_CC=gcc
 
-  HOST_CC = $(CC)
-  HOST_CXX = $(CXX)
-  HOST_LD = $(LD)
-  HOST_LDXX = $(LDXX)
+  HOST_CC ?= $(CC)
+  HOST_CXX ?= $(CXX)
+  HOST_LD ?= $(LD)
+  HOST_LDXX ?= $(LDXX)
 
   ifdef EXTRA_DEFINES
   DEFINES += $(EXTRA_DEFINES)
@@ -315,14 +339,6 @@
   IS_GIT_FOLDER = true
   endif
 
-  ifeq ($(SYSTEM),Linux)
-  OPENSSL_REQUIRES_DL = true
-  endif
-
-  ifeq ($(SYSTEM),Darwin)
-  OPENSSL_REQUIRES_DL = true
-  endif
-
   ifeq ($(HAS_PKG_CONFIG),true)
   OPENSSL_ALPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.2 openssl
   OPENSSL_NPN_CHECK_CMD = $(PKG_CONFIG) --atleast-version=1.0.1 openssl
@@ -341,11 +357,6 @@
   ZLIB_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/zlib.c -lz $(LDFLAGS)
   PROTOBUF_CHECK_CMD = $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/protobuf.cc -lprotobuf $(LDFLAGS)
 
-  ifeq ($(OPENSSL_REQUIRES_DL),true)
-  OPENSSL_ALPN_CHECK_CMD += -ldl
-  OPENSSL_NPN_CHECK_CMD += -ldl
-  endif
-
   endif # HAS_PKG_CONFIG
 
   PERFTOOLS_CHECK_CMD = $(CC) $(CFLAGS) $(CPPFLAGS) -o $(TMPOUT) test/build/perftools.c -lprofiler $(LDFLAGS)
@@ -509,9 +520,6 @@
   OPENSSL_MERGE_LIBS += $(LIBDIR)/$(CONFIG)/libboringssl.a
   # need to prefix these to ensure overriding system libraries
   CPPFLAGS := -Ithird_party/boringssl/include $(CPPFLAGS)
-  ifeq ($(OPENSSL_REQUIRES_DL),true)
-  LIBS_SECURE = dl
-  endif # OPENSSL_REQUIRES_DL
   else # EMBED_OPENSSL=false
   ifeq ($(HAS_PKG_CONFIG),true)
   OPENSSL_PKG_CONFIG = true
@@ -531,10 +539,7 @@
   CPPFLAGS += -DTSI_OPENSSL_ALPN_SUPPORT=0
   LIBS_SECURE = $(OPENSSL_LIBS)
   endif # HAS_SYSTEM_OPENSSL_NPN
-  ifeq ($(OPENSSL_REQUIRES_DL),true)
-  LIBS_SECURE += dl
   PC_LIBS_SECURE = $(addprefix -l, $(LIBS_SECURE))
-  endif # OPENSSL_REQUIRES_DL=true
   endif # EMBED_OPENSSL
   endif # NO_SECURE
 
@@ -1438,7 +1443,7 @@
   $(LIB${lib.name.upper()}_OBJS): CXXFLAGS := -Ithird_party/boringssl/include $(CXXFLAGS) -fvisibility=hidden
   $(LIB${lib.name.upper()}_OBJS): CPPFLAGS += -DOPENSSL_NO_ASM -D_GNU_SOURCE
   % elif lib.zlib:
-  $(LIB${lib.name.upper()}_OBJS): CFLAGS := $(CFLAGS) -Wno-sign-conversion -Wno-conversion -Wno-unused-value -Wno-implicit-function-declaration -fvisibility=hidden
+  $(LIB${lib.name.upper()}_OBJS): CFLAGS := $(CFLAGS) -Wno-sign-conversion -Wno-conversion -Wno-unused-value -Wno-implicit-function-declaration $(W_NO_SHIFT_NEGATIVE_VALUE) -fvisibility=hidden
   % else:
   % endif
 
diff --git a/templates/binding.gyp.template b/templates/binding.gyp.template
index 932b70e88e42fb91486406853c42b32af19e279e..71276e75e53ab952ee46e448a0de6a2e2f429652 100644
--- a/templates/binding.gyp.template
+++ b/templates/binding.gyp.template
@@ -7,7 +7,7 @@
   # This file can be regenerated from the template by running
   # tools/buildgen/generate_projects.sh
 
-  # Copyright 2015, Google Inc.
+  # Copyright 2015-2016, Google Inc.
   # All rights reserved.
   #
   # Redistribution and use in source and binary forms, with or without
@@ -209,5 +209,16 @@
         ]
       },
       % endfor
+      {
+        "target_name": "action_after_build",
+        "type": "none",
+        "dependencies": [ "<(module_name)" ],
+        "copies": [
+          {
+            "files": [ "<(PRODUCT_DIR)/<(module_name).node"],
+            "destination": "<(module_path)"
+          }
+        ]
+      }
     ]
   }
diff --git a/templates/package.json.template b/templates/package.json.template
index ec6827ef7663020f6b98733370e15c02f8cbc26b..ed4dca50e28039c2dc4e8a7b92499ce27f9f6513 100644
--- a/templates/package.json.template
+++ b/templates/package.json.template
@@ -24,12 +24,14 @@
       "lint": "node ./node_modules/jshint/bin/jshint src/node/src src/node/test src/node/interop src/node/index.js",
       "test": "./node_modules/.bin/mocha src/node/test && npm run-script lint",
       "gen_docs": "./node_modules/.bin/jsdoc -c src/node/jsdoc_conf.json",
-      "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test"
+      "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha src/node/test",
+      "preinstall": "npm install node-pre-gyp",
+      "install": "./node_modules/.bin/node-pre-gyp install --fallback-to-build"
     },
     "dependencies": {
-      "bindings": "^1.2.0",
       "lodash": "^3.9.3",
       "nan": "^2.0.0",
+      "node-pre-gyp": "^0.6.19",
       "protobufjs": "^4.0.0"
     },
     "devDependencies": {
@@ -47,6 +49,14 @@
     "engines": {
       "node": ">=0.10.13"
     },
+    "binary": {
+      "module_name": "grpc_node",
+      "module_path": "./build/Release/",
+      "host": "https://storage.googleapis.com/",
+      "remote_path": "grpc-precompiled-binaries/node/{name}/v{version}",
+      "package_name": "{node_abi}-{platform}-{arch}.tar.gz",
+      "module_path": "src/node/extension_binary"
+    },
     "files": [
       "LICENSE",
       "src/node/README.md",
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
index acb265b3086369bdd9974f6eb9fb5261a9be9ecd..66269ae7578d29c8db3f878508dd69499d581a27 100644
--- a/test/cpp/qps/driver.cc
+++ b/test/cpp/qps/driver.cc
@@ -1,6 +1,6 @@
 /*
  *
- * Copyright 2015, Google Inc.
+ * Copyright 2015-2016, Google Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -161,6 +161,8 @@ std::unique_ptr<ScenarioResult> RunScenario(
   // where class contained in std::vector must have a copy constructor
   auto* servers = new ServerData[num_servers];
   for (size_t i = 0; i < num_servers; i++) {
+    gpr_log(GPR_INFO, "Starting server on %s (worker #%d)", workers[i].c_str(),
+            i);
     servers[i].stub = WorkerService::NewStub(
         CreateChannel(workers[i], InsecureChannelCredentials()));
     ServerArgs args;
@@ -188,6 +190,8 @@ std::unique_ptr<ScenarioResult> RunScenario(
   // where class contained in std::vector must have a copy constructor
   auto* clients = new ClientData[num_clients];
   for (size_t i = 0; i < num_clients; i++) {
+    gpr_log(GPR_INFO, "Starting client on %s (worker #%d)",
+            workers[i + num_servers].c_str(), i + num_servers);
     clients[i].stub = WorkerService::NewStub(
         CreateChannel(workers[i + num_servers], InsecureChannelCredentials()));
     ClientArgs args;
diff --git a/test/cpp/qps/qps_worker.cc b/test/cpp/qps/qps_worker.cc
index 0bacc11b23fdc69e27175d37c6e7a89ab47c7c29..6316605aafd15bef66cf14c9a0858aad157bb714 100644
--- a/test/cpp/qps/qps_worker.cc
+++ b/test/cpp/qps/qps_worker.cc
@@ -61,6 +61,11 @@ namespace grpc {
 namespace testing {
 
 static std::unique_ptr<Client> CreateClient(const ClientConfig& config) {
+  gpr_log(GPR_INFO, "Starting client of type %s %s %d",
+          ClientType_Name(config.client_type()).c_str(),
+          RpcType_Name(config.rpc_type()).c_str(),
+          config.payload_config().has_bytebuf_params());
+
   switch (config.client_type()) {
     case ClientType::SYNC_CLIENT:
       return (config.rpc_type() == RpcType::UNARY)
@@ -81,6 +86,9 @@ static std::unique_ptr<Client> CreateClient(const ClientConfig& config) {
 static void LimitCores(int cores) {}
 
 static std::unique_ptr<Server> CreateServer(const ServerConfig& config) {
+  gpr_log(GPR_INFO, "Starting server of type %s",
+          ServerType_Name(config.server_type()).c_str());
+
   if (config.core_limit() > 0) {
     LimitCores(config.core_limit());
   }
@@ -171,22 +179,29 @@ class WorkerServiceImpl GRPC_FINAL : public WorkerService::Service {
     if (!args.has_setup()) {
       return Status(StatusCode::INVALID_ARGUMENT, "");
     }
+    gpr_log(GPR_INFO, "RunClientBody: about to create client");
     auto client = CreateClient(args.setup());
     if (!client) {
       return Status(StatusCode::INVALID_ARGUMENT, "");
     }
+    gpr_log(GPR_INFO, "RunClientBody: client created");
     ClientStatus status;
     if (!stream->Write(status)) {
       return Status(StatusCode::UNKNOWN, "");
     }
+    gpr_log(GPR_INFO, "RunClientBody: creation status reported");
     while (stream->Read(&args)) {
+      gpr_log(GPR_INFO, "RunClientBody: Message read");
       if (!args.has_mark()) {
+        gpr_log(GPR_INFO, "RunClientBody: Message is not a mark!");
         return Status(StatusCode::INVALID_ARGUMENT, "");
       }
       *status.mutable_stats() = client->Mark(args.mark().reset());
       stream->Write(status);
+      gpr_log(GPR_INFO, "RunClientBody: Mark response given");
     }
 
+    gpr_log(GPR_INFO, "RunClientBody: Returning");
     return Status::OK;
   }
 
@@ -202,24 +217,31 @@ class WorkerServiceImpl GRPC_FINAL : public WorkerService::Service {
     if (server_port_ != 0) {
       args.mutable_setup()->set_port(server_port_);
     }
+    gpr_log(GPR_INFO, "RunServerBody: about to create server");
     auto server = CreateServer(args.setup());
     if (!server) {
       return Status(StatusCode::INVALID_ARGUMENT, "");
     }
+    gpr_log(GPR_INFO, "RunServerBody: server created");
     ServerStatus status;
     status.set_port(server->port());
     status.set_cores(server->cores());
     if (!stream->Write(status)) {
       return Status(StatusCode::UNKNOWN, "");
     }
+    gpr_log(GPR_INFO, "RunServerBody: creation status reported");
     while (stream->Read(&args)) {
+      gpr_log(GPR_INFO, "RunServerBody: Message read");
       if (!args.has_mark()) {
+        gpr_log(GPR_INFO, "RunServerBody: Message not a mark!");
         return Status(StatusCode::INVALID_ARGUMENT, "");
       }
       *status.mutable_stats() = server->Mark(args.mark().reset());
       stream->Write(status);
+      gpr_log(GPR_INFO, "RunServerBody: Mark response given");
     }
 
+    gpr_log(GPR_INFO, "RunServerBody: Returning");
     return Status::OK;
   }
 
diff --git a/tools/jenkins/grpc_interop_node/build_interop.sh b/tools/jenkins/grpc_interop_node/build_interop.sh
index 3b69715c9af983effb7126e0312b1588483a0e5c..526dd61fdfc2725d9b38ce1ef987ea0b7e97918a 100755
--- a/tools/jenkins/grpc_interop_node/build_interop.sh
+++ b/tools/jenkins/grpc_interop_node/build_interop.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -45,4 +45,4 @@ make install-certs
 
 # build Node interop client & server
 npm install -g node-gyp
-(npm install && node-gyp rebuild)
+npm install --unsafe-perm --build-from-source
diff --git a/tools/run_tests/build_node.sh b/tools/run_tests/build_node.sh
index faa7b624b8e44e8a7e62cc4fd6aa48944f7f4877..8f2ab4413a20cde4f084866d2e2df7966c66d730 100755
--- a/tools/run_tests/build_node.sh
+++ b/tools/run_tests/build_node.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-# Copyright 2015, Google Inc.
+# Copyright 2015-2016, Google Inc.
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
@@ -36,4 +36,4 @@ CONFIG=${CONFIG:-opt}
 # change to grpc repo root
 cd $(dirname $0)/../..
 
-npm install --unsafe-perm
+npm install --unsafe-perm --build-from-source