José Cabo
José Cabo

Reputation: 6769

How to run Puppeteer on a Docker container on a MacOS Apple Silicon M1 arm64 host

I'm unable to run my unit tests on Puppeteer inside of a Docker container. My host is a MacOS with Apple Silicon M1 (arm64) chip. I've also tried to follow the instructions from the Puppeteer Github documentation but they are meant for amd64 instead. Any suggestion?

Upvotes: 9

Views: 13548

Answers (3)

If you share your codebase with not ARM members, you could use if ARM then in your Dockerfile to be compatible with your colleagues.

# ... common dockerfile commands shared for all architectures

# Chromium fix for ARM (like Apple M1) 
RUN if [ "$(uname -m)" = "aarch64" ] ; then \
        apt-get update && apt-get install -y chromium ; \
        export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ; \
        export PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser ; \
    fi

Upvotes: 0

Steven Spungin
Steven Spungin

Reputation: 29099

The only way I have been able to run Puppeteer via Docker on Apple Silicon is by.

  1. Creating the image by specifying amd64 platform
FROM --platform=linux/amd64 node:18
  1. Enabling "Use Rosetta" support in Docker Desktop

enter image description here

  1. Following the current Puppeteer Docker instructions to manually install Chromium instead of installing it as part of Puppeteer.

Modified Puppeteer Docker File

This example simplifies the current Puppeteer instructions. I removed their create user instructions because one is included in the node image. Also, they wipe out the apt registry after installing Chromium, preventing you from installing anything else. Add that back if you feel the need.


FROM --platform=linux/amd64 node:18

# We can define environment variables here
# instead of specifying them when we launch Puppeteer. 
# The path may change depending on your platform and installed binary.
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome-stable


RUN apt-get update \
    && apt-get install -y wget gnupg \
    && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/googlechrome-linux-keyring.gpg \
    && sh -c 'echo "deb [arch=amd64 signed-by=/usr/share/keyrings/googlechrome-linux-keyring.gpg] 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-khmeros fonts-kacst fonts-freefont-ttf libxss1 \
      --no-install-recommends

# If you desire to run in Chrome sandbox mode, change to a non-root user, and make sure you launch your image with `cap_add: SYS_ADMIN`.

USER node

Running As Non-Root With Chromium Sandbox Enabled

  1. Set a non-root user in your dockerfile
  2. Enable cap_add: SYS_ADMIN when launching your image.

Example docker compose

services: {
  node: {
    cap_add:
      - SYS_ADMIN
  }
}

With the environment variables set in DOCKERFILE, cap_add set, and a non-root user running, you can just fire up Puppeteer with it's default paranoid sandboxing.

In DOCKERFILE

# Use the non-root user that comes with `node` image,
# Or another if you wish.
USER node

In your Javascript

const browser = await puppeteer.launch({
   headless: true
})

Running As Root And Bypassing Chromium Sandbox

If you would rather rough it and disable Chromium Sandbox, just run as the default root user, and turn off the sandbox protection.

  1. Do not set a user in your dockerfile
  2. When launching Puppeteer, disable the sandbox
const browser = await puppeteer.launch({
   args: [
     '--no-sandbox'
   ],
   headless: true
})

References:

https://github.com/puppeteer/puppeteer/blob/main/docker/Dockerfile

Upvotes: 7

José Cabo
José Cabo

Reputation: 6769

Installing puppeteer NPM package directly won't work for some reason and sadly the official Puppeteer documentation in GitHub is not compatible with arm64 architecture.

This is how I've prepared my Dockerfile:

FROM node:16

RUN apt-get update \
 && apt-get install -y chromium \
    fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst fonts-freefont-ttf libxss1 \
    --no-install-recommends
    
USER node # non-root user that comes with `node` images.

WORKDIR /app

COPY --chown=node package.json .
COPY --chown=node package-lock.json .

ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
ENV PUPPETEER_EXECUTABLE_PATH /usr/bin/chromium

RUN npm install

COPY --chown=node . /app

The Dockerfile as it is configured is native to Apple Silicon (linux/arm64) as well as native for amd64 (linux/amd64) architectures.

You also must pass --no-sandbox argument to the browser on your code:

export async function createBrowserInstance(): Promise<puppeteer.Browser> {
    return await puppeteer.launch({
        args: ['--no-sandbox'], // Required.
        headless: true,
    });
}

The extra flag disables the browser's dev sandboxing, so make sure to access only trusted pages. Otherwise it's fine. Please, somebody from the future, please feel free to edit this answer or comment how to get rid of the --no-sandbox argument.

Upvotes: 18

Related Questions