Hi everyone, this is my first time trying to create a small tutorial where I will show how to run a Node application with Docker, Nginx with load balancing, Postgres, and Docker Compose. If you have any feedback at the end, please let me know; I will appreciate it. Also, English is not my native language, so if you find grammar errors, sorry about it.
I will assume that you have Docker and Docker Compose installed on your machine. This tutorial was made on Ubuntu OS.
If you don't have Docker and Docker Compose installed, you can visit the Docker website to see how to install Docker. For Docker Compose, you can check this link: Docker Compose.
Let's get down to business.
1 - Create a route using Hono.js
Hono.js is a nice framework that helps us build Node apps. This article does not aim to teach Hono.js
; I will create a simple route to print Hello World!
First, create a new folder on your machine using the command line:
~ mkdir example
Enter your folder and let's install some libraries. I will be using pnpm, a package manager like npm or yarn but with some differences. If you don't have it installed, you can access the straightforward instructions in the documentation: pnpm
Install hono and typescrypt
~ pnpm init // To create the initial package.json
~ pnpm install hono
~ pnpm install @hono/node-server
~ pnpm install -D typescript
After installing these packages, you should run the command below to create a default tsconfig.json
, which is the TypeScript configuration of the project.
~ npx tsc --init
Now create a folder called src
, and inside it, create the file server.ts
:
~ mkdir src
~ touch src/server.ts
In the server.ts
add the code below.
1 import { Hono } from "hono";
2 import { serve } from "@hono/node-server";
3 import { logger } from "hono/logger";
4
5 const Server = {
6 start: () => {
7 const app = new Hono();
8 app.use(logger());
9
10 app.get("/", (c) => c.text("Hello World!"));
11
12 serve(
13 {
14 port: 3000,
15 fetch: app.fetch,
16 },
17 (info) => {
18 console.log(`Listening on http://localhost:${info.port}`);
19 },
20 );
21 },
22 };
23
24 Server.start();
Here we have a simple server that will provide an endpoint returning "Hello World!".
You should install the tsx
package to run the server:
~ pnpm install -D tsx
And in the package.json
under the scripts section, we add this line:
"start": "npx tsx ./src/server.ts"
Now in your terminal you can run:
~ pnpm start
Now our app is running. You just need to open your browser and go to the address http://localhost:3000/ and you will see the page with the Hello World!.
2 Configure docker for our server
Now we will configure a Dockerfile for our app. I will not explain the Docker syntax in detail, but this is a simple Dockerfile.
In the root of the project create a Dockerfile
~ touch Dockerfile
Now put this code inside the Dockerfile:
1 FROM node:22-alpine
2
3 COPY . /app
4
5 WORKDIR /app
6
7 RUN npm install -g pnpm
8
9 RUN pnpm install
10
11 EXPOSE 3000
12
13 CMD ["pnpm", "start"]
1 - FROM
- This will tell the name of the image that we want to use.
2 - COPY
- We are making a copy of everything from our project and putting in the app folder from our container.
3 - Workdir
- We tell the container from what folder we want to execute our commands.
4 - RUN
- We use this to install dependencies inside our container, in this case we are installing pnpm.
5 - EXPOSE
- Here we say what port our container will use. PS: Is not the port of our machine (host).
6 - CMD
- We pass which commands the container should run at the end.
Now create the build from our dockerfile:
~ docker build -t my-server:1.0 . // You can put the name you want
The dot .
is for the command run the Dockerfile from the folder you are.
If you want to see the container just use the command:
~ docker ps -a
Now run the container with this command:
~ docker run my-server:1.0
Access the http://localhost:3000/ you will see a problem:
We cannot access the address because the app is running only inside the container, and we didn't specify which port from our machine the container should use.
Let's try again, but now mapping the port from the container to a port on our machine (host):
~ docker run -p 3000:3000 my-server:1.0
Now if you access the http://localhost:3000/ you'll see that the app running because we told Docker to map port 3000 from the container to port 3000 on our machine. PS: You could use another port here if you want.
Now the app is working with Docker; let's configure Nginx.
3 - Configure Nginx with load balancing
Nginx is an open-source web server software used for reverse proxy, load balancing, and caching.
In the root of the project, create a new folder called nginx , and inside it, create the Dockerfile
.
~ mkdir nginx
~ touch nginx/Dockerfile
Before we configure Nginx, let's see it working on our machine. Put this code inside the Dockerfile
.
1 FROM nginx:latest
2
3 EXPOSE 80
Here we are getting the latest image from nginx from Dockerhub and exposing the port 80 from the container. Now we can build and run it. Go to the nginx folder in your terminal and run:
~ docker build -t my-server-nginx:1.0 . // My container will have the name my-server-nginx:1.0
~ docker run -p 9000:80 my-server-nginx:1.0
The second command runs our Nginx, listening to port 9000 on our machine. If you access http://localhost:9000/ you'll see the page:
Now let's configure load balancing for our Nginx. First, inside the nginx folder, create nginx.conf
~ touch nginx.conf
Let's edit the ´nginx.conf' and add these lines:
The upstream backend
is responsible for our load balancing. Load balancing is a technique that distributes network traffic across multiple servers to prevent bottlenecks and improve performance. For more information, you can visit this link.
PS: The proxy_pass
domain (line 18) should match the name you put in the upstream (line 7), in this case, backend
.
In the upstream, we specify that we'll have two servers. Every time we access http://myserver.local Nginx will redirect the request to one of these two servers, preventing server overload.
In the Nginx Dockerfile, we can add a new line after the instruction FROM
:
COPY ./nginx.conf /etc/nginx/nginx.conf
Your Dockerfile
should look like this:
To make it easy to run everything together, let's configure Docker Compose.
4 - Configure Docker Compose
In the root of the project, create docker-compose.yml
and add the code below:
version: '3.9'
services:
app:
build:
context: .
volumes:
- ./:/usr/app
- /usr/app/node_modules
ports:
- 3000:3000
networks:
- app-network
app2:
build:
context: .
volumes:
- ./:/usr/app
- /usr/app/node_modules
ports:
- 3001:3000
networks:
- app-network
nginx:
build:
context: ./nginx/.
container_name: my-server-nginx
ports:
- "80:80"
depends_on:
- app
- app2
networks:
- app-network
networks:
app-network:
driver: bridge
Note that we have two instances or containers for our server, app
and app2
. If you change these names, you'll need to change the name in the nginx.conf
inside the upstream.
Also, in nginx.conf
inside the upstream, you should put the port that you exposed in your container, in our case, 3000
. I don't know why, but when I tried to put the port of my machine, it didn't work.
To access http://myserver.local/ you will need to edit your /etc/hosts
file.
~ sudo vim /etc/hosts
At the end of the file, add this line:
127.0.0.1 myserver.local
Now save the file and try to exit from vim 🤣
In your terminal run:
~ docker-compose up
This command will start the apps and Nginx. Now you can access the route at http://myserver.local/, and every time you refresh the page, you will see that the request is sent to one of the servers, sometimes to app
and sometimes to app2
.
5 - Configure Postgres
I will not connect the app with the database; I will show just how to set up Postgres, and you can connect as you wish.
In our docker-compose.yml
, add the following lines after the Nginx section:
postgres:
container_name: postgres
restart: always
image: postgres
ports:
- 5432:5432
environment:
POSTGRES_USER: docker
POSTGRES_PASSWORD: postgres
POSTGRES_DB: myserver
networks:
- app-network
volumes:
- postgres:/data/postgres
volumes:
postgres:
You can stop the containers using this command and start again:
~ docker-compose down // stop the containers
~ docker-compose up
To view your database, you can install dbeaver I have been using DBeaver for a long time (thanks bop for recommending it 🙏🏻).
Notice that we have three environment variables that Docker will use. You can change their values if you want.
In DBeaver, you can click on new database connection, select Postgres, and fill in the database, password, and user fields with the values with environment variables in the docker-compose.yml
.
Notice that the:
1 - host
= localhost # default
2 - username
= docker # This comes from POSTGRES_USER
inside docker-compose.yml
3 - password
= postgres # This comes from POSTGRES_PASSWROD
inside docker-compose.yml
4 - database
= myserver # This comes from POSTGRES_DB
inside docker-compose.yml
With all this, you will have a simple start project with hono.js + nginx + postgres + docker.
And that's it, folks! Thank you for reading this far. If you liked the post, please give it a like.
If you have feedback or questions, let me know.
Repository: github
SDG
Top comments (0)