Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hapi server on heroku fails to bind port

I'm working on a Hapi server for a ReactJS app but when I try to deploy to Heroku, I get the R10 error "Failed to bind to $PORT within 60 seconds of launch". What is going on? I'm using process.env.PORT. I also tried parseInt() around it. Also tried disabling varying packages. The build is successful always.

In the Heroku logs, I see the console log from the index.js ("Hapi running on ...") but then the R10 error shows up and the server restarts, then crashes.

==> 🌎 Hapi Production Server (API) is listening on http://localhost:14316 2016-01-22T15:10:33.947571+00:00 heroku[web.1]: Stopping process with SIGKILL 2016-01-22T15:10:33.947571+00:00 heroku[web.1]: Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds of launch 2016-01-22T15:10:34.737554+00:00 heroku[web.1]: State changed from starting to crashed 2016-01-22T15:10:34.724233+00:00 heroku[web.1]: Process exited with status 137

This all runs fine locally when I run with NODE_ENV=production

src/server.js

import Hapi from 'hapi';
import Inert from 'inert';
import jwt from 'hapi-auth-jwt2';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { RoutingContext, match } from 'react-router';
import { Provider } from 'react-redux';
import createRoutes from './routes';
import configureStore from './store/configureStore';
import Html from './Html';

const PROTOCOL = 'http://';
const SERVER_HOST = process.env.HOST || 'localhost';
const SERVER_PORT = process.env.PORT || 3000;
const API_HOST = process.env.API_HOST || 'localhost';
const API_PORT = process.env.API_PORT || 8000;

export default function(callback) {

    const server = new Hapi.Server();

    server.connection({
    host: SERVER_HOST,
    port: SERVER_PORT,
    labels: ['api'],
    // routes: {
    //   cors: {
    //     origin: [PROTOCOL + API_HOST + ':' + API_PORT]
    //   }
    // }
  });

    server.connections[0].name = 'API';

    server.register([
        { register: Inert },
        { register: jwt },
        // {
      //   register: api,
      //   routes: {
      //     prefix: '/api'
      //   }
      // }
    ], (err) => {
    if(err) {
      console.error('ERROR:', err)
      throw err;
    }

        server.route({
        method: 'GET',
        path: '/{param*}',
        handler: {
          directory: {
            path: 'static'
          }
        }
      });

        server.ext('onPreResponse', (request, reply) => {

            if (typeof request.response.statusCode !== 'undefined') {
        return reply.continue();
      }

            const assets = {
                javascript: {
                    main: '/dist/bundle.js'
                }
            };

          const store = configureStore();
          const routes = createRoutes(store);

          // this gets called if server side rendering/routing has problems and errors
          function hydrateOnClient() {
            reply('<!doctype html>\n' +
              renderToString(<Html assets={assets} store={store} />)).code(500);
          }

          match({ routes, location: request.path }, (error, redirectLocation, renderProps) => {

            if (redirectLocation) {

              res.redirect(301, redirectLocation.pathname + redirectLocation.search)

            } else if (error) {
              console.error('ROUTER ERROR:', error) // eslint-disable-line no-console
              hydrateOnClient();

            } else if (!renderProps) {

              // in some cases this would act as a 404 but that should be handled in the routes
              hydrateOnClient();

            } else {

              const component = (
                <Provider store={store}>
                  <RoutingContext {...renderProps} />
                </Provider>
              );

              reply('<!doctype html>\n' +
                renderToString(<Html assets={assets} component={component} store={store} />)
                    );
            }
          });
        });
    });

    return server.start((err) => {

        if(err) {
            console.log(err);
            throw err;
        }

        callback(server)
    });

}

index.js

require('babel-core/register');

global.__DEVELOPMENT__ = process.env.NODE_ENV !== 'production';
global.__SERVER__ = true;
global.__CLIENT__ = false;

const server = require('./src/server');

server(server => {
   for (var key of Object.keys(server.connections)) {
     console.info('==> 🌎 Hapi Production Server (' + server.connections[key].name + ') is listening on', server.connections[key].info.uri);
  }
});
like image 339
zebapy Avatar asked Jan 22 '16 15:01

zebapy


People also ask

What PORT should I use for Heroku?

Heroku expects a web application to bind its HTTP server to the port defined by the $PORT environment variable. Many frameworks default to port 8080, but can be configured to use an environment variable instead.

Does heroku set PORT?

Heroku sets the PORT variable that you are supposed to bind, and listens on tcp/80.

What is process env PORT?

PORT || 3000 means: process. env. PORT means the PORT number you manually set. 3000 is the default port . If you havent set it manually then it will listen to 3000.


1 Answers

process.env.HOST was undefined on Heroku, and for some reason it didn't like localhost as the host, which caused the issue.

I simply removed the host var all together, so connection looks like:

server.connection({
  port: process.env.PORT || 3000,
  labels: ['api'],
})
like image 116
zebapy Avatar answered Oct 26 '22 23:10

zebapy