In this post, you will learn about how to get Puppeteer to work under Docker.
I recently tried to dockerise an old hobby project and unsurprisingly, a couple of things broke. Some of these were fairly simple fixes so I won’t go into their details - but I will go into a fairly obscure one, which was caused by puppeteer. Regarding the application itself, it does a few things, but for the purposes of this article, let’s just say that it renders some reports using Puppeteer and NodeJS (while running as a .NET app).
Dance Monkey, Dance Monkey, Dance…Wait, What?
If you haven’t heard of Puppeteer, it’s basically a NodeJS library that allows you to run (and control) an instance of headless chrome, i.e., - an instance of Google chrome without a UI. In this project, I was using it to render PDF exports of reports. Running natively, it worked like a charm. But under docker, well… I’m writing this article after all, aren’t I?
Hold Up, Let’s See Your Docker Setup First
I thought you might say that! Well, first here’s the dockerfile.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["MyApplication.API/MyApplication.API.csproj", "MyApplication.API/"]
COPY ["MyApplication.Common/MyApplication.Common.csproj", "MyApplication.Common/"]
COPY ["MyApplication.Data/MyApplication.Data.csproj", "MyApplication.Data/"]
COPY ["MyApplication.Logic/MyApplication.Logic.csproj", "MyApplication.Logic/"]
RUN dotnet restore "MyApplication.API/MyApplication.API.csproj"
COPY . .
WORKDIR "/src/MyApplication.API"
RUN dotnet build "MyApplication.API.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "MyApplication.API.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApplication.API.dll", "--environment", "Docker"]
# Install node and npm
RUN apt-get update
RUN apt-get install nodejs=12.22.5~dfsg-2~11u1 -y
RUN apt-get install npm=7.5.2+ds-2 -y
ENV NODE_ENV=production
# Install node modules for reports
WORKDIR "/app/Reports/jsreport"
RUN npm install
# Set the directory back so dotnet is able to run the application
WORKDIR "/app"
Basically, I load the ASP.NET 6 base image, build the solution, publish it, install require node modules and set the dotnet
program as the entry point of the container.
Then I build an image using docker build -t myimage -f ./MyApplication.API/Dockerfile .
Note that in order for the build step in the docker file to work, the image has to be created at the solution level and not the project level - since all of the projects need to be built. Hence why I’m using the solution directory when we build the image.
Then I run a container using docker run -p 127.0.0.1:80:80/tcp myimage
.
So far so good! But after invoking one of the reports yields an internal server error.
Show Me the Errors!
Well, since this is running under docker (and this is just a hobby app, I’m using old-school rolling file logs), I actually need to sh
into the container first to see the logs. So first to get the id of the container: docker ps
, and once we have the id of the container: docker exec -it CONTAINER_ID /bin/bash
. Opening the error log reveals:
Jering.Javascript.NodeJS.InvocationException: Failed to launch chrome!
/app/Reports/jsreport/node_modules/puppeteer/.local-chromium/linux-609904/chrome-linux/chrome:
error while loading shared libraries: libnss3.so: cannot open shared object file:
No such file or directory
TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md
Error: Failed to launch chrome!
Interesting! Following the link provided and scrolling down to Running Puppeteer under Docker tells you that we basically need to add a step in our dockerfile to install the necessary libraries (particularly libnss3.so
) in order for headless chrome to run. The dockerfile in their example is a bit verbose with some unnecessary steps, but the real magic is basically just:
# Install latest chrome dev package and fonts to support major charsets
# (Chinese, Japanese, Arabic, Hebrew, Thai and a few others)
# Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer
# installs, work.
RUN apt-get update \
&& apt-get install -y wget gnupg \
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >>
/etc/apt/sources.list.d/google.list' \
&& apt-get update \
&& apt-get install -y google-chrome-stable fonts-ipafont-gothic fonts-wqy-zenhei
fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/*
And voila! Puppeteer is now working under Docker. Hope this helped someone out, or was at least an interesting read. :)
Catch ya!