This post will demonstrate how we deployed our four simple Node.js backend applications into a load-balanced setup using Nginx. We utilized Docker images for this and connected all five containers via a Docker network.
Next, we’ll simplify the process by using Docker Compose, making everything shorter and easier. Let’s take a look at the Dockerfiles we created earlier:
1: The Dockerfile we created for our backend service:
FROM node:19-alpine WORKDIR /usr/src/app COPY package*.json ./ RUN npm install COPY . . EXPOSE 5050 CMD [ "node", "index.js" ]
2: Nginx web server we use for load balancing
FROM nginx:stable-alpine COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
3: Configuration file for Nginx
upstream backend {
server backend-1:5050;
server backend-2:5050;
server backend-3:5050;
server backend-4:5050;
}
server {
listen 80;
include /etc/nginx/mime.types;
location / {
proxy_pass http://backend/;
}
}
As shown in the configuration above, we define four backend servers under the upstream directive. Nginx, by default, uses the Round Robin load balancing algorithm. Since we haven’t specified any other algorithm, Nginx will use this one. These four servers will be on the same Docker network, so any request to localhost:80 will be forwarded to one of the four servers based on the Round Robin algorithm.

Now let’s create the docker-compose file to create the necessary containers.
version: '3'
services:
backend_1:
build: ../server
environment:
- PORT=5050
networks:
- loadbalancing
backend_2:
build: ../server
environment:
- PORT=5050
networks:
- loadbalancing
backend_3:
build: ../server
environment:
- PORT=5050
networks:
- loadbalancing
backend_4:
build: ../server
environment:
- PORT=5050
networks:
- loadbalancing
nginx:
build: ../nginx
ports:
- "80:80"
networks:
- loadbalancing
depends_on:
- backend_1
- backend_2
- backend_3
- backend_4
networks:
loadbalancing:
We’ve created four virtual backend servers as specified in the nginx.conf file, as seen in the Docker Compose file above. Let’s go over the parameters they take, in order:
- build: This points to the project directory image mentioned earlier. The Docker Compose file is located in a folder named
docker. From there, we access the Dockerfile in theserverfolder and use it to build the image. - environment: This is where we specify the environment variables needed by the container at runtime. In our example, we defined the
PORTvariable to specify the port on which the backend server will run. Our backend will be accessible on port 5050. - networks: This parameter creates a virtual network in Docker, allowing containers with the same network value to communicate with each other. Since the Nginx web server needs to reach the backend servers on port 80, we place all containers on the same network. We’ve named this network loadbalancing.
Now that all our operations are complete, we can set up our four virtual servers and one Nginx web server with the following command:
docker-compose up -d
After the command runs, the image on docker desktop will look like this:

Now when we send a request from localhost:80 to the web server, we will see a response from the servers.
So, is it possible to simplify the docker-compose file a bit more?
Yes, it’s possible to modify our Docker Compose file without creating a new service for each server we want to set up. Let’s take a look at the following example:
version: '3'
services:
backend:
build: ../server
environment:
- PORT=5050
deploy:
replicas: 4
networks:
- loadbalancing
nginx:
build: ../nginx
container_name: nginx
ports:
- "80:80"
networks:
- loadbalancing
depends_on:
- backend
networks:
loadbalancing:
In the Docker Compose file above, instead of creating a separate service for each backend server, we used the deploy key to streamline the process. By setting the replicas parameter, we specify how many instances of the relevant service should be running when the Compose file is executed. However, the process doesn’t end there. We’ll also need to make some adjustments to the nginx.conf file. The updated configuration file should look like this:
upstream backend {
server backend:5050;
}
server {
listen 80;
resolver 127.0.0.11 valid=5s;
include /etc/nginx/mime.types;
location / {
proxy_pass http://backend/;
}
}
Once you’ve completed these operations, you can restart the services using the docker-compose up -d command and proceed with running your tests.


Leave a Reply to Farhan Khan Cancel reply