How to Fix Puppeteer’s Browser Launch Issue in Docker Containers with Node-HTML-to-Image

How to Fix Puppeteer’s Browser Launch Issue in Docker Containers with Node-HTML-to-Image

Featured on Hashnode

When working with image generation in a Node.js application, one of the most popular libraries is node-html-to-image, which internally uses Puppeteer to convert HTML content into image formats like JPEG or PNG. While this library works well in most cases, running it inside a Docker container can sometimes lead to browser launch failures, particularly with Puppeteer.

The Problem: Browser Launch Error in Docker

While generating an image in a Dockerized environment, you might encounter the following error:

codeError: Unable to launch browser, error message: Failed to launch the browser process! spawn /root/.cache/puppeteer/chrome/linux-115.0.5790.102/chrome-linux64/chrome ENOENT

This error occurs because node-html-to-image uses Puppeteer by default, which in turn attempts to download and use its own version of Chromium. In many cases, this Chromium version might not be compatible with your Docker environment, leading to errors such as the one mentioned above.

Root Cause

The key issue here is that Puppeteer tries to download and use a Chromium version that doesn't always align with the operating system or dependencies available in your Docker container. Particularly in Alpine-based containers, Chromium dependencies are strict, and downloading an unsupported version causes launch failures.

The Solution: Control Puppeteer Versions and Chromium Binaries

To solve this issue, I forced node-html-to-image to use puppeteer-core, which gives full control over the Chromium version. Instead of allowing Puppeteer to manage Chromium downloads, we specify the exact version supported by Alpine and configure Puppeteer to use that version.

This ensures that there are no unexpected version mismatches that could lead to breakages.

Steps to Implement the Fix

1. Modify the Dockerfile

First, we need to modify the Dockerfile to explicitly install a specific version of Chromium that is compatible with the Alpine base image.

# Prevent Puppeteer from downloading Chromium
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true

# Install the specific version of Chromium and required dependencies
RUN apk add --no-cache \
    chromium=124.0.6367.78-r0 \
    nss \
    freetype \
    harfbuzz \
    ca-certificates \
    ttf-freefont

Here, we:

  • Set PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true to skip Puppeteer’s default Chromium installation.

  • Install a version of Chromium that is available in the Alpine repository.

  • Install dependencies like nss, freetype, harfbuzz, and others required by Chromium to function properly.

2. Update the Puppeteer Execution Path

Next, we configure node-html-to-image to use puppeteer-core and explicitly set the executablePath for Puppeteer to point to the installed Chromium binary.

const puppeteerCore = require('puppeteer-core');
const nodeHtmlToImage = require('node-html-to-image');

// Generate email image
try {
    await nodeHtmlToImage({
        output: `uploads/emails/${email._id}.jpeg`,
        html: emailInformation.html,
        type: 'jpeg',
        puppeteer: puppeteerCore,
        puppeteerArgs: {
            headless: 'shell',
            args: ['--no-sandbox'],
            executablePath: process.env.PUPPETEER_EXECUTABLE_PATH,  // Path to the manually installed Chromium
        },
    });
} catch (puppeteerError) {
    console.error(`Puppeteer error: ${puppeteerError.message}`);
    return res
        .status(500)
        .send('Error generating image: ' + puppeteerError.message);
}

In this implementation:

  • puppeteer-core is used instead of the full puppeteer package, giving us full control over the Chromium version.

  • executablePath is set to the PUPPETEER_EXECUTABLE_PATH environment variable, which points to the manually installed version of Chromium.

3. Configure the Environment Variables

Ensure that you set the PUPPETEER_EXECUTABLE_PATH environment variable in your Docker runtime to match the installed Chromium binary's location. For Alpine, this would typically be:

bashCopy codeENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser

This tells Puppeteer exactly where to find the Chromium executable, avoiding any ambiguity.

Why This Fix Works

By combining puppeteer-core with a specific version of Chromium installed via the Alpine package manager, we eliminate Puppeteer’s dependency on its internal Chromium download. This ensures:

  • Version Control: You explicitly control which version of Chromium is used, avoiding incompatibility issues.

  • Stability: The versions of Chromium and Puppeteer are tightly coupled, reducing the likelihood of random breakages due to version mismatches.

  • Portability: The Docker container remains portable and reproducible, as it relies on a fixed version of Chromium rather than downloading new versions dynamically.

Conclusion

When running node-html-to-image inside a Docker container, especially on Alpine Linux, you might encounter browser launch issues due to Puppeteer’s Chromium version incompatibility. By using puppeteer-core, installing the correct Chromium version manually, and configuring the executable path, you can avoid these issues and ensure consistent, reliable image generation.

This approach guarantees that the versions never change, making your solution stable across different environments.