Chromium Browser Path Issue with Puppeteer on DigitalOcean App Platform

I have a Node.js app that creates PDF files using Puppeteer and it works fine locally but fails when deployed to DigitalOcean App Platform. The error says it can’t find Chromium at the path I specified.

My Setup:

  • DigitalOcean App Platform deployment
  • Node.js 20.x with latest Puppeteer
  • Using .aptfile to install Chromium

Here’s my PDF generation code:

const express = require('express');
const puppeteer = require('puppeteer');
const handlebars = require('handlebars');
const fs = require('fs');

const server = express();
const PORT = 5000;

// Template compilation
const templateSource = fs.readFileSync('./views/report.hbs', 'utf8');
const template = handlebars.compile(templateSource);

server.use(express.json());

server.post('/create-report', async (req, res) => {
  try {
    const reportData = req.body;
    
    // Process data
    const netAmount = reportData.items.reduce((total, item) => total + item.cost, 0);
    const taxAmount = netAmount * 0.2;
    const finalTotal = netAmount + taxAmount;
    
    const currentDate = new Date().toLocaleDateString();
    
    // Create HTML from template
    const htmlContent = template({
      data: reportData,
      netAmount,
      taxAmount,
      finalTotal,
      date: currentDate
    });
    
    // Launch browser and create PDF
    const browserInstance = await puppeteer.launch({
      args: [
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '--disable-dev-shm-usage'
      ],
      executablePath: '/layers/digitalocean_apt/apt/usr/bin/chromium-bsu'
    });
    
    const newPage = await browserInstance.newPage();
    await newPage.setContent(htmlContent);
    const pdfData = await newPage.pdf({ format: 'A4', printBackground: true });
    await browserInstance.close();
    
    res.contentType('application/pdf');
    res.send(pdfData);
    
  } catch (err) {
    console.error('PDF creation failed:', err);
    res.status(500).json({ message: 'Could not create PDF' });
  }
});

server.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

My .aptfile contains:

chromium
chromium-common
chromium-sandbox
fonts-liberation
libatk-bridge2.0-0
libasound2

Error message I’m getting:

Browser was not found at the configured executablePath (/layers/digitalocean_apt/apt/usr/bin/chromium-bsu)

The browser path seems wrong but I’m not sure what the correct path should be for DigitalOcean App Platform. Has anyone solved this before?

Your executablePath is pointing to chromium-bsu, which is actually a space shooter game, not the browser. When you install chromium through .aptfile on DigitalOcean App Platform, the real browser lives at /layers/digitalocean_apt/apt/usr/bin/chromium. I hit this exact issue six months ago and wasted hours figuring out the right path. Just change your executablePath to /layers/digitalocean_apt/apt/usr/bin/chromium and you’re good. The path can sometimes vary between deployments, so throw in some debugging code to check if the file exists before launching puppeteer.

You’re installing the wrong package. Use chromium-browser in your .aptfile, not just chromium. Had the same headache last year - DigitalOcean’s buildpack puts chromium-browser at /layers/digitalocean_apt/apt/usr/bin/chromium-browser. Switch your .aptfile to chromium-browser and update your executablePath. Or just drop the executablePath completely and let puppeteer use its built-in chromium - works better on containers sometimes. Your current path won’t work anyway since chromium-bsu has nothing to do with web browsing.

hit this exact issue last week. run which chromium in your build logs to see where it’s actually installed. DigitalOcean’s apt layer messes with paths sometimes. better yet, just use puppeteer.launch() without executablePath - the bundled chromium works way more reliably than system packages on DO.

Had this exact nightmare on DO App Platform 8 months ago. The problem? The apt layer path structure changes based on when your app builds and which buildpack version runs. Don’t hardcode executablePath - set it dynamically by checking multiple locations. I wrote a function that tests common paths like /usr/bin/chromium, /usr/bin/chromium-browser, and the layers path before launching. But honestly? Just remove executablePath completely. Puppeteer’s bundled chromium beats system packages on DO every time. You’ll get a slightly bigger slug, but no more path headaches.