Using nginx to serve multiple nodejs applications on different domains

Serving multiple web applications for different domains can be easily achieved via the use of nginx. In this post we will go through an example on how to achieve this with an nginx docker container.

To host a node.js web application on an EC2 instance (or any other VPS), we need to serve the app under port 80 (and 443 for SSL) and then point the domain to the address of our EC2 instance. However, once we want to serve a second application for a different domain on the same instance we run into a problem: Only one of the two applications can take control over a given port.

In this article we want to have a look at how we can use nginx to host multiple applications that serve different domains on the same server.

Initial State and Problem

Let's say we have two simple web applications that are written in node.js.

  • A cats & cookies application that contains valuable information about cats and cookies and should be served under the domain cats-n-cookies.com.
const express = require('express')
const app = express()
const port = 80

app.get('/', (req, res) => res.send('Cats & Cookies'))

app.listen(port, '0.0.0.0', () =>
  console.log(`Cats & Cookies listening on port ${port}!`)
)

  • A dogs & cookies application for the dog lovers that should be served under the domain dogs-n-cookies.com.
const express = require('express')
const app = express()
const port = 80

app.get('/', (req, res) => res.send('Dogs & Cookies'))

app.listen(port, '0.0.0.0', () =>
  console.log(`Dogs & Cookies listening on port ${port}!`)
)

As long as we only have one of those applications running we are able to serve all http requests for the given domain:

One app serving all Requests on Port 80

The client makes a request to cats-n-cookies.com which uses the http port 80 and points to our server.

But if we want to also point dogs-n-cookies.com to the same server, we run into the problem that we can't run both applications on the same port:

Error: listen EADDRINUSE: address already in use 0.0.0.0:80

And even if that was possible, how would our server know which application should serve the request that went to port 80?

Theoretical problem where two apps run on port 80. Server couldnt tell where to route a request.

As we can already see from the illustration, we can solve this problem by routing requests to the applications based on the requested url and not only the port.

In the following we will achieve this with the help of nginx.

Goal

Nginx allows us to smartly route requests on our server. We can use nginx to route requests that were received from cat-n-cookies.com to the cats application and requests that were received from dogs-n-cookies.com to the dogs application.

Nginx will be the application running on port 80 on our machine, while the actual cats and dogs apps run on different ports.

Our Goal: nginx routes requests to the specific app

Configuring NGINX

In this article we will use nginx in a docker container, but our approach would also work with a locally installed nginx.

With docker installed we can easily start an nginx container on our host network:

docker container run --net=host nginx

Nginx should then return its welcome page when called (http://localhost without a port, to test it locally): nginx displays a "Welcome to nginx!" page on success

What we have to do now is to configure nginx to route the requests to each application. This is done via the

nginx.conf
file:

events {
	worker_connections 1024;
}
http {
	server {
	  listen 80;
	  server_name cats-n-cookies.com;
      proxy_pass  http://127.0.0.1:3000;
    }
    server {
      listen      80;
      server_name dogs-n-cookies.com;
      proxy_pass  http://127.0.0.1:3001;
    }
}

To tell our nginx to use this configuration file we can mount it directly into the container to the location where nginx expects its default configuration file:

(use this command from the directory where the nginx.conf file is located)

docker container run \
	--net=host \
    -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf \
	nginx:alpine

Now if we start our cats application on port 3000 and and our dogs application on port 3001 we can test if our configuration works.

To try this locally we need to add two entries to our

/etc/hosts
file. This step is of course not needed if you're working on a remote server and the domains already point to that server.

127.0.0.1       localhost
# ADD ENTRIES BELOW
127.0.0.1       cats-n-cookies.com
127.0.0.1       dogs-n-cookies.com
# END

# keep rest of the file as is
# ...

If we now go to cats-n-cookies.com the request will be routed to the local application running on port 3000. If we go to dogs-n-cookies.com the request will be routed to the dogs application.

Conclusions

Nginx makes it very easy to serve multiple domains on a single server. In this case nginx acts as a proxy that routes requests depending on the hostname in the request to the specific application. Especially with the help of docker, installing and running nginx is a breeze.