Anyone using Puppeteer self-hosted?

I’m self-hosting on Ubuntu (and sometimes Amazon Linux 2023). Trying to use puppeteer from the code module to render HTML into PDF. It almost always fails to start. But after a couple of tries it works and then keeps working. Any suggestions?

I’ve tried inserting an execute command module with sudo snap refresh --edge chromium before the code module. I have no idea if that has any effect. Doesn’t seem to help or hurt. Mostly just indicates that I don’t know what I’m doing. :slight_smile:

Here’s the script in the code module. Maybe there’s another puppeteer call I need to make before I start trying to render anything?

Any help would be great. Thanks!

Lee

const puppeteer = require('puppeteer');

// Create a browser instance
const browser = await puppeteer.launch({ headless: true, executablePath: "/snap/bin/chromium", args: ['--disable-gpu', '--disable-setuid-sandbox', '--no-sandbox', '--no-zygote']});

// Create a new page
const page = await browser.newPage();

// To reflect CSS used for screens instead of print
await page.emulateMediaType('print');

//Get HTML content from HTML file
await page.setContent($input.item.json.html, { waitUntil: 'domcontentloaded' });

// Downlaod the PDF
const pdf = await page.pdf({
  path: `/tmp/${$input.item.json.client.invoiceNum}.pdf`,
  margin: { top: '1in', right: '1in', bottom: '1in', left: '1in' },
  printBackground: true,
  format: 'Letter',
  landscape: true
  
});

// Close the browser instance
await browser.close();

return $input.item;

Information on your n8n setup

  • n8n version: 1.64.3
  • Database (default: SQLite): Postgres
  • n8n EXECUTIONS_PROCESS setting (default: own, main): main
  • Running n8n via (Docker, npm, n8n cloud, desktop app): npm
  • Operating system: Ubuntu 24.04.1 LTS

Hey @Lee_S,

Have you tried to launch puppeteer outside of n8n to see if there are any prompts or anything else that needs to be done? I have tried to use puppeteer once and I think I had to set an option for it to work. There is a puppeteer community node have you tried using that instead?

Great suggestion @Jon ! Just tried that and didn’t see anything too wonky. But it gives me an avenue to explore that I hadn’t thought of.

For example, in my script I use
await page.setContent($input.item.json.html, { waitUntil: 'domcontentloaded' });
to actually render the page. I wonder if I’m waiting for the wrong thing.

Another thing I should probably do is insert an annoying number of console.log lines to see where it’s stalling. That might illuminate where it’s running into problems.

Anyway, I’ll report back if/when I figure this out. Thanks again for your help!

Lee

More info on this for future googlers:

First, if you’re self-hosting on Ubuntu you might run into the same apparmor problem that I ran into. See
https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md

To resolve:

  1. sudo cp /etc/apparmor.d/chrome /etc/apparmor.d/chrome-snap
  2. Edit /etc/apparmor.d/chrome-snap and change the executable to /snap/bin/chromium
  3. sudo service apparmor reload

Next, when you call chromium from the code module it’ll help to add a few command line args. I have no idea if this is a good or even complete list, but it seems to work for me.

const browser = await puppeteer.launch({ headless: "new", executablePath: "/snap/bin/chromium", args: ['--disable-gpu', '--disable-setuid-sandbox', '--no-sandbox', '--no-zygote', '--disable-notifications']});

After that, here’s how I render the HTML that’s in $json.html in landscape mode.

// Create a new page
const page = await browser.newPage();

// To reflect CSS used for screens instead of print
await page.emulateMediaType('print');

//Get HTML content from HTML file
await page.setContent($input.item.json.html);

await waitTillHTMLRendered(page);

// Downlaod the PDF
const pdf = await page.pdf({
  path: `/tmp/${$input.item.json.client.invoiceNum}.pdf`,
  margin: { top: '1in', right: '1in', bottom: '1in', left: '1in' },
  printBackground: true,
  format: 'Letter',
  landscape: true
  
});

// Close the browser instance
await browser.close();

return $input.item;

That puts the pdf in /tmp, and you can use the read files module to pull it into the N8N flow.

4 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.