Logo

dev-resources.site

for different kinds of informations.

Running Puppeteer in a Docker container on Raspberry Pi

Published at
12/2/2023
Categories
docker
raspberrypi
chrome
chromium
Author
moozzyk
Author
7 person written this
moozzyk
open
Running Puppeteer in a Docker container on Raspberry Pi

Puppeteer is a Node.js module that allows interacting with a (headless) web browser programmatically. This is extremely useful for automating website testing, generating screenshots and PDFs of web pages or programmatic form submission.

Docker offers numerous benefits including a standardized environment, isolation, and rapid deployment to name a few. These benefits might be why you’d want to run Puppeteer inside a Docker container. Unfortunately, doing so on Raspberry Pi is not straightforward. There are a few issues that make it harder than usual. Luckily, they are all solvable. Let’s take a look.

Problem 1: Chromium included in Puppeteer does not work on Raspberry Pi

Puppeteer by default downloads a matching version of Chromium which is guaranteed to work out of the box on supported platforms. Unfortunately, Chromium does not currently provide an arm build that works on Raspberry Pi and running stock Puppeteer on Raspberry Pi will end up with a crash. This can be solved by installing Chromium with apt-get install chromium -y and telling Puppeteer to use it by passing the executablePath: '/usr/bin/chromium' to the launch() function as follows:



const browser = await puppeteer.launch({
     executablePath: '/usr/bin/chromium',
     args: []
 });


Enter fullscreen mode Exit fullscreen mode

When doing this, Puppeteer no longer should need to download Chromium as it will be using the version installed with apt-get, so it makes sense to skipping this step by setting the corresponding environment variable in the Dockerfile:



ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true


Enter fullscreen mode Exit fullscreen mode

This will significantly reduce the time needed to install node modules.

Problem 2: The base image installs an old version of Chromium

Some node Docker images are based on old distributions that contain only older versions of Chromium. Most notably the node:16 image is based on buster. If you use this base image the only version of Chromium you will be able to install with apt-get is 90.0.4430.212-1. Unfortunately this version doesn’t work in a Docker container – it just hangs indefinitely. Moving to the node:16-bullseye base image allows installing a much newer version of Chromium (108.0.5359.124) where this is no longer a problem.

Problem 3: Puppeteer crashes on launch

Puppeteer will not launch in a Docker container without additional configuration. Chromium is not able to provide sandboxing when running inside a container so it needs to be launched at least with the --no-sandbox argument. Otherwise it will crash with the following error message:



Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted


Enter fullscreen mode Exit fullscreen mode

Sandbox is a security feature and running without a sandbox is generally discouraged. Unfortunately, running without a sandbox appears to be currently the only way to run Puppeteer inside a Docker container. In the past the --no-sandbox option required running Puppeteer as root, only increasing the risk. Luckily, this no longer seems to be the case – it is possible now to launch puppeteer with the --no-sandbox option as a non-privileged user.
There are a few more options that might be worth exploring if launching Puppeteer inside a container fails:

  • --disable-gpu – disables GPU hardware acceleration (which is usually not available when running in Docker)
  • --disable-dev-shm-usage – prevents from using shared RAM (/dev/shm/)
  • --disable-setuid-sandbox – disabled setuid sandbox

Putting everything together

The information provided above should be all that is needed to be build a Docker image for a Node.js app that uses Puppeteer and runs on Raspberry Pi. Below is an example Dockerfile for such a Docker image. It contains comments to make it easy to notice how the solutions discussed above were applied.



# Ensure an up-to-date version of Chromium 
# can be installed (solves Problem 2)
FROM node:16-bullseye 
# Install a working version of Chromium (solves Problem 1)
RUN apt-get update
RUN apt-get install chromium -y
ENV HOME=/home/app-user
RUN useradd -m -d $HOME -s /bin/bash app-user 
RUN mkdir -p $HOME/app
WORKDIR $HOME/app
COPY package*.json ./
COPY index.js ./
RUN chown -R app-user:app-user $HOME
# Run the container as a non-privileged user (discussed in Problem 3)
USER app-user
# Make `npm install` faster by skipping 
# downloading default Chromium (discussed in Problem 1)
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
RUN npm install
CMD [ "node", "index.js" ]


Enter fullscreen mode Exit fullscreen mode

Because the application also requires a couple modifications to how the headless browser is launched here is a small example application illustrating these changes with comments:



const puppeteer = require('puppeteer');
(async() => {
    const browser = await puppeteer.launch({
        // use Chromium installed with `apt` (solves Problem 1)
        executablePath: '/usr/bin/chromium',
        args: [
            // run without sandbox (solves Problem 3)
            '--no-sandbox',
            // other launch flags (discussed in Problem 3)
            // '--disable-gpu,
            // '--disable-dev-shm-usage',
            // '--disable-setuid-sandbox',
        ]
    });
    const page = await browser.newPage();
    await page.goto('https://www.google.com/', {waitUntil: 'networkidle2'});
    let e = await page.$('div#hplogo');
    let p = await e?.getProperty('title');
    if (p) {
      console.log(`Today's doodle: ${await p.jsonValue()}`);
    } else {
      console.log('No Doodle today :(');
    }
    browser.close();
})();


Enter fullscreen mode Exit fullscreen mode

Finally, here is the output of this application when run in a container:

Output generated when executing the example

Both the application and the Dockerfile are also available on Github

Conclusion

Running Puppeteer inside a Docker container is tricky – especially, when doing so on Raspberry Pi. The post discussed the key obstacles and provided solutions to overcome them. In addition, a demo containerized app was included to illustrate the main points.


💙 If you liked this article...

I publish a weekly newsletter for software engineers who want to grow their careers. I share mistakes I’ve made and lessons I’ve learned over the past 20 years as a software engineer.

Sign up here to get articles like this delivered to your inbox.

https://www.growingdev.net/

chromium Article's
30 articles in total
Favicon
Avoiding a "Host Permission" Review Delay When Publishing a Chrome Extension
Favicon
Proyect Fugu
Favicon
Proyecto Fugu: Revolucionando las aplicaciones web progresivas (PWA)
Favicon
How To Download An Old Version of Chromium
Favicon
Why gRPC is not natively supported by Browsers
Favicon
Running Puppeteer in a Docker container on Raspberry Pi
Favicon
Chromium Spelunking: Connecting to Proxies
Favicon
🌐 Installing Chromium on Mac Apple M2 Pro (Tutorial)
Favicon
Chromium Spelunking: The IO Thread
Favicon
Chromium Spelunking: A Stuck Task
Favicon
Chromium Spelunking: Threads and Tasks
Favicon
Chromium Spelunking: Creating a Request
Favicon
Chromium Spelunking: Churl
Favicon
Chromium Spelunking: Life and Times
Favicon
Chromium Spelunking: Getting Started
Favicon
Google Chrome の CVE 脆弱性: 2022 年 12 月頃のリリースバージョンまで
Favicon
CVE vulnerabilities on Google Chrome prior to releases around on Dec. 2022
Favicon
Stop Chromium from asking about default browser
Favicon
Message loop
Favicon
Share highlight in Chrome/ Chromium
Favicon
Useful Visual Studio Code Extension for Developing Chromium
Favicon
Fix sameSite cookie issue in Cypress by installing an older version of Chromium
Favicon
Why HTTP streaming upload matters on Web browser
Favicon
Google refusing to connect to other sites?!
Favicon
Chromium console stopped working. - What's wrong? How do I fix it?
Favicon
Chrome Omnibox
Favicon
Using Chromium to replace Electron Applications
Favicon
Snapd doesn't work on WSL2 Ubuntu20.04
Favicon
Prevent chromedp Chromium zombie processes from stacking
Favicon
Overcoming SameSite cookie issue in Cypress when running on Chrome or Edge

Featured ones: