DEV Community

Cover image for How to dockerize a hapijs application
Joseph Jude
Joseph Jude

Posted on • Updated on

How to dockerize a hapijs application

Docker has drastically enhanced how we develop and deploy web applications. With docker, we can isolate our development environment and resemble the production environment as close as possible. When our development environment resembles the production environment, we deploy with confidence.

We can even go a step further. With automated tests, and few other tools, we can automate the deployment. That brings ease and confidence — for us, for our teams, and for our clients. In software development, ease and confidence is value.

In this tutorial, I'm going to show you how to dockerize an existing hapijs application.

Hapijs is a nodejs framework from Walmart. They use it to power their e-commerce system. If you require an introduction to hapijs, you can read my introductory post about hapijs.

First, we are going to create a simple hapijs application. To do that let us create the package.json file.

{
  "name": "sample-hapijs-inside-docker",
  "version": "1.0.0",
  "description": "Hapijs app in docker container",
  "main": "index.js",
  "scripts": {
      "start": "node src/server.js",
      "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Joseph Jude",
  "license": "ISC",
  "dependencies": {
    "hapi": "14.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

This is a common package.json file. I want you to notice just two lines here.

We are using a start script: node src/server.js. It will become clear as we go along. Just keep this in mind.

Also, notice that we are using hapijs version 14.

Now let us create the server.js file.

const Hapi = require('hapi');

const server = new Hapi.Server();
server.connection({ port: 8080 });

server.route({
    method: 'GET',
    path: '/',
    handler: function (request, reply) {
        reply('Hello, docker!');
    }
});

server.start((err) => {

    if (err)
        throw err;

    console.log('Server running at:', server.info.uri);
});
Enter fullscreen mode Exit fullscreen mode

Nothing special here. We instantiate a Hapi server at port 8080 and print Hello docker when the server comes up.

We can pull an existing nodejs docker container and build our application on top of it. Or, we can build a container from scratch. We are going to build it from scratch.

We are going to use alpine as the base OS for our container. Why alpine? Alpine is both small and secure.

We are going to create a text file named Dockerfile in the same directory where we created the above two files. Dockerfile contains the instructions to build the container.

So our directory structure looks like this:

├── Dockerfile
├── package.json
└── server.js
Enter fullscreen mode Exit fullscreen mode

Let us build the Dockerfile step by step. It looks like this to start with:

FROM alpine:3.4
RUN apk update && apk upgrade
RUN apk add nodejs
RUN rm -rf /var/cache/apk/*
Enter fullscreen mode Exit fullscreen mode

What does these lines mean?

Line 1: We are basing our container from alpine

Line 2: Update the OS

Line 3: Add nodejs

Line 4: remove apk cache

If we build a container with these lines, we will get a nodejs container. We want a hapijs one. So let us go on.

Remember, we already created a package.json and server.js files. Let us copy them into the container. Append these lines to Dockerfile.

COPY . /src
RUN cd /src; npm install
EXPOSE 8080
CMD ["node", "/src/server.js"]
Enter fullscreen mode Exit fullscreen mode

Let me explain these lines.

Line 1: We are instructing the docker engine to copy the entire directory to a folder src within the docker container.

Line 2: Install the required dependencies. This will install hapijs.

Line 3: Notice that in the server.js we are running the hapijs server at 8080. In this line we instruct docker to expose 8080 port to the outside world, so that we can access the server from our host machine.

Line 4: Lastly we instruct docker to run the server with the command node /src/server.js. Commands and parameters are given in the fashion as in this line.

Our complete Dockerfile looks like this:

FROM alpine:3.4
RUN apk update && apk upgrade
RUN apk add nodejs
RUN rm -rf /var/cache/apk/*

COPY . /src
RUN cd /src; npm install
EXPOSE 8080
CMD ["node", "/src/server.js"]
Enter fullscreen mode Exit fullscreen mode

We can build the container. Open the terminal and issue this command:

docker build -t jjude/hapi .
Enter fullscreen mode Exit fullscreen mode

Docker needs a dockerfile to build a container. We can either provide the full path or instruct to use the dockerfile in the current directory. We are instructing the build process to use the Dockerfile from the current directory.

It is also a good practice to tag the container with <user_name>/<application_name>.

When you issue this command, it will start pulling the necessary components (os, nodejs, hapijs) from the web and build a container. You will start seeing the output that goes like this:

Sending build context to Docker daemon 5.632 kB
Step 1/8 : FROM alpine:3.4
 ---> 0766572b4bac
Step 2/8 : RUN apk update && apk upgrade
 ---> Using cache
 ---> edfde7516f9b
Step 3/8 : RUN apk add nodejs
 ---> Running in 4d786fc5787b
(1/4) Installing libgcc (5.3.0-r0)
(2/4) Installing libstdc++ (5.3.0-r0)
(3/4) Installing libuv (1.9.1-r0)
(4/4) Installing nodejs (6.7.0-r0)
Executing busybox-1.24.2-r13.trigger
OK: 41 MiB in 15 packages
 ---> 25426b5e81e2
Removing intermediate container 4d786fc5787b
Step 4/8 : RUN rm -rf /var/cache/apk/*
 ---> Running in 2db1ab7c47b5
 ---> efbdf59a5e3a
Removing intermediate container 2db1ab7c47b5
Step 5/8 : COPY . /src
 ---> f67db160cffb
Removing intermediate container b452bcf97da6
Step 6/8 : RUN cd /src; npm install
 ---> Running in eebeb5c18070
sample-hapijs-inside-docker@1.0.0 /src
`-- hapi@14.0.0
  +-- accept@2.1.3
  | `-- boom@4.2.0
  +-- ammo@2.0.3
  | `-- boom@4.2.0
  +-- boom@3.2.2
  +-- call@3.0.4
  | `-- boom@4.2.0
  +-- catbox@7.1.3
  | +-- boom@4.2.0
  | `-- joi@10.2.0
  +-- catbox-memory@2.0.4
  +-- cryptiles@3.1.1
  | `-- boom@4.2.0
  +-- heavy@4.0.3
  | +-- boom@4.2.0
  | `-- joi@10.2.0
  +-- hoek@4.1.0
  +-- iron@4.0.4
  | `-- boom@4.2.0
  +-- items@2.1.1
  +-- joi@9.2.0
  | +-- isemail@2.2.1
  | `-- moment@2.17.1
  +-- kilt@2.0.2
  +-- mimos@3.0.3
  | `-- mime-db@1.26.0
  +-- peekaboo@2.0.2
  +-- shot@3.4.0
  | `-- joi@10.2.0
  +-- statehood@4.1.0
  +-- subtext@4.3.0
  | +-- boom@4.2.0
  | +-- content@3.0.3
  | | `-- boom@4.2.0
  | +-- pez@2.1.4
  | | +-- b64@3.0.2
  | | +-- boom@4.2.0
  | | `-- nigel@2.0.2
  | |   `-- vise@2.0.2
  | `-- wreck@10.0.0
  |   `-- boom@4.2.0
  `-- topo@2.0.2

npm WARN sample-hapijs-inside-docker@1.0.0 No repository field.
 ---> e04821112bdf
Removing intermediate container eebeb5c18070
Step 7/8 : EXPOSE 8080
 ---> Running in 6889ad302d2f
 ---> adfd88fe97ca
Removing intermediate container 6889ad302d2f
Step 8/8 : CMD node /src/server.js
 ---> Running in dcde9b8ab986
 ---> 07786de22668
Removing intermediate container dcde9b8ab986
Successfully built 07786de22668
Enter fullscreen mode Exit fullscreen mode

It should end with Successfully built.

We have the container. You can check it by issuing docker images. It should show something like this:

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
jjude/hapi          latest              07786de22668        23 minutes ago      41.1 MB
Enter fullscreen mode Exit fullscreen mode

As you can see, the basic container that can execute our server.js is of 41.1 MB.

Now we need to bring up this container (run it). Issue this command docker run -p 8080:8080 -d jjude/hapi.

What does this command do?

We are running our container jjude/hapi and binding the container port 8080 to the host port 8080.

What if you want to use a different port, say 9000? You can do that with -p 9000:8080. The format goes like this:

-p <host port>:<container port>
Enter fullscreen mode Exit fullscreen mode

It will come back to the terminal command prompt. How to check if our little server is running?

Issue a curl command.

curl http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

This should output this:

Hello, docker!
Enter fullscreen mode Exit fullscreen mode

That's it. We successfully dockerised our sample hello docker program.

What if you want to go into the container and play around? Docker provides an option to enter into a running container. To do that, we need to know the id of the running container. Issue docker ps. It will show an output like this:

CONTAINER ID        IMAGE               COMMAND                 CREATED             STATUS              PORTS                    NAMES
2fb1cb0abb92        jjude/hapi          "node /src/server.js"   23 minutes ago      Up 23 minutes       0.0.0.0:8080->8080/tcp   cocky_hawking
Enter fullscreen mode Exit fullscreen mode

The first parameter is the container id. Copy it and issue the command:

docker exec -it 2fb1cb0abb92 /bin/sh
Enter fullscreen mode Exit fullscreen mode

Use the correct container id from the previous output.

Now you are into the container. You can stop and restart the server. The source code lives in /src/ folder.

You can checkin these three files (package.json, server.js, Dockerfile) to a repository and ask a member of your team to checkout the repository and build the container. It will work exactly the same way. That's the magic of Docker!

Interested in learning hapijs with typescript? Subscribe to my newsletter

Originally posted at jjude.com

Top comments (0)