How to run Google Chrome (Headless) and NodeJS in a Docker container

Engineering

In a recent post, I described how to debug Google Chrome (Headless) on Amazon ECS. This time we are going to look at how to run Google Chrome (Headless) inside a Docker container.

While I used Amazon ECS, the same applies to Docker running on any host from local to another hosted provider.

The base image here was for NodeJS, however, any other Alpine based distribution will work just as well.

The first step is to create your Dockerfile. First, we add the edge repositories to the base Node image and then we use apk to install Chromium and all the required dependencies.

As I was using puppeteer in NodeJS, which typically downloads its own version of Chromium, we use the PUPPETEER_SKIP_CHROMIUM_DOWNLOAD variable to tell puppeteer that we wish for it to skip this process.

# Dockerfile
FROM node:8.11.1-alpine

RUN echo "http://dl-2.alpinelinux.org/alpine/edge/main" > /etc/apk/repositories
RUN echo "http://dl-2.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories
RUN echo "http://dl-2.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories

# install chromium
RUN apk -U --no-cache \
    --allow-untrusted add \
    zlib-dev \
    chromium \
    xvfb \
    wait4ports \
    xorg-server \
    dbus \
    ttf-freefont \
    grep \ 
    udev \
    && apk del --purge --force linux-headers binutils-gold gnupg zlib-dev libc-utils \
    && rm -rf /var/lib/apt/lists/* \
    /var/cache/apk/* \
    /usr/share/man \
    /tmp/* \
    /usr/lib/node_modules/npm/man \
    /usr/lib/node_modules/npm/doc \
    /usr/lib/node_modules/npm/html \
    /usr/lib/node_modules/npm/scripts

ENV CHROME_BIN=/usr/bin/chromium-browser
ENV CHROME_PATH=/usr/lib/chromium/
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1

# Application specific run commands 
RUN npm install -g pm2

ADD ./code /code

EXPOSE 3000

WORKDIR /code

RUN npm install

CMD ["pm2-runtime", "start", "/code/config/ecosystem.config.json"]

Once you have created your Dockerfile the next step is to build the image. We can use the docker command line tool here to build the image for us. Specify a tag for your image after the -t parameter.

docker build -t docker-chromium .
Sending build context to Docker daemon 344.8 MB
Step 1/14 : FROM node:8.11.1-alpine
 ---> e707e7ad7186
Step 2/14 : RUN echo "http://dl-2.alpinelinux.org/alpine/edge/main" > /etc/apk/repositories
 ---> Using cache
 ---> 557bab3760cd
Step 3/14 : RUN echo "http://dl-2.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories
 ---> Using cache
 ---> 8c46e4f2ffa4
Step 4/14 : RUN echo "http://dl-2.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
 ---> Using cache
 ---> 7670bbe976d3
Step 5/14 : RUN apk -U --no-cache 	--allow-untrusted add     zlib-dev     chromium     xvfb     wait4ports     xorg-server     dbus     ttf-freefont     grep     udev     && apk del --purge --force linux-headers binutils-gold gnupg zlib-dev libc-utils     && rm -rf /var/lib/apt/lists/*     /var/cache/apk/*     /usr/share/man     /tmp/*     /usr/lib/node_modules/npm/man     /usr/lib/node_modules/npm/doc     /usr/lib/node_modules/npm/html     /usr/lib/node_modules/npm/scripts
 ---> Using cache
 ---> f488a2a34b33
Step 6/14 : ENV CHROME_BIN /usr/bin/chromium-browser
 ---> Using cache
 ---> d8563c002a0d
Step 7/14 : ENV CHROME_PATH /usr/lib/chromium/
 ---> Using cache
 ---> 50eb7afa75bf
Step 8/14 : ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD 1
 ---> Using cache
 ---> 3d099e5f721b
Step 9/14 : RUN npm install -g pm2
 ---> Using cache
 ---> aa18179668a9
Step 10/14 : ADD ./code /code
 ---> Using cache
 ---> 743af3e0d498
Step 11/14 : EXPOSE 3000
 ---> Using cache
 ---> 86787a35da07
Step 12/14 : WORKDIR /code
 ---> Using cache
 ---> e0cb0fc32a01
Step 13/14 : RUN npm install
 ---> Using cache
 ---> cb970f3b75b2
Step 14/14 : CMD pm2-runtime start /code/config/ecosystem.config.json
 ---> Using cache
 ---> 0441aa5551a8
Successfully built 0441aa5551a8

The first time you run the build you will see a little more output than this as the required dependencies are downloaded.

The final step now is to run your Docker container. If you wish to test this locally you can simply issue the docker run command. If you wish to forward any ports you can use the -p command for this.

docker run -p 3000:3000 docker-chromium
[2018-07-07 02:58:11] PM2 log: Launching in no daemon mode
[2018-07-07 02:58:12] PM2 log: Starting execution sequence in -fork mode- for app name:sumo-microsumo-mogul-csv id:0
[2018-07-07 02:58:12] PM2 log: App name:docker-chromium id:0 online
Listening!

That’s it! Now your Docker container is running and you can connect to your application via http://localhost:3000/