How to dockerize a hapijs application
Tags: hapijs, docker
Published: 2017-01-26 15:59:35

---
title: How to dockerize a hapijs application
published: true
description: A step by step guide to dockerizing an existing hapijs application
cover_image: https://cdn.jjude.com/tech/2017-hapijs-in-docker.jpg
tags: hapijs, docker
---

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](http://hapijs.com/) 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](https://jjude.com/hapi-with-tsc/) 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"
  }
}
```

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);
});
```

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](https://alpinelinux.org/) 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
```

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/*
```

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"]
```

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"]
```

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

```
docker build -t jjude/hapi .
```
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
```

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
```
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>
``` 

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
```

This should output this:

```
Hello, docker!
```

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
```

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

```
docker exec -it 2fb1cb0abb92 /bin/sh
```

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](https://jjude.com/hapijs)**

_Originally posted at [jjude.com](https://www.jjude.com/hapijs-on-docker/)_