DEV Community

Pankaj Sharma
Pankaj Sharma

Posted on • Originally published at build-and-scale.hashnode.dev

Chromium + Puppeteer Broke After ASP.NET 8 10 Upgrade — The Hidden Snap Trap in Docker

We upgraded a service from ASP.NET 8 to 10. No application code changed—but our Puppeteer-based document pipeline completely broke.

The root cause wasn’t .NET. It was a subtle base image change:

Debian → Ubuntu, which replaced a working Chromium install with a non-functional Snap wrapper inside Docker.


The Setup

Our document generation pipeline works like this:

  • Render HTML using Puppeteer

  • Use Chromium as the headless browser

  • Post-process assets with Sharp

  • Run everything inside a Docker container

This setup worked reliably on ASP.NET 8.


What Changed?

As part of the upgrade:

  • From: mcr.microsoft.com/dotnet/aspnet:8.0 (Debian-based)

  • To: mcr.microsoft.com/dotnet/aspnet:10.0 (Ubuntu-based)

At first glance, nothing looked risky. But this OS-level change had a critical side effect.


The Real Problem (Root Cause)

On Debian:

  • apt install chromium installs a real Chromium binary

On Ubuntu:

  • apt install chromium installs a Snap wrapper, not the browser itself

That distinction is the entire problem.


Why This Breaks in Docker

Snap packages are not just alternative installers—they rely on a runtime model that assumes:

  • a full init system (systemd)

  • background services (snapd)

  • elevated privileges

Docker containers deliberately avoid all of these.

So while apt install chromium appears successful, it installs a deferred execution wrapper, not an actual browser.

When Puppeteer tries to launch Chromium, there is nothing usable to execute.


Symptoms (and Why They’re Misleading)

Installation appears successful:

apt install chromium
Enter fullscreen mode Exit fullscreen mode

But Puppeteer fails with:

Failed to launch the browser process!
Enter fullscreen mode Exit fullscreen mode

On inspection:

which chromium
# /usr/bin/chromium-browser

file /usr/bin/chromium-browser
# POSIX shell script, not a binary
Enter fullscreen mode Exit fullscreen mode

This is the key signal:

Chromium is not actually installed—only a wrapper script exists.


What We Tried (That Didn’t Work)

These approaches all failed:

  • Installing additional libraries (libx11, libnspr4, fonts, etc.)

  • Switching Puppeteer executable paths

  • Using snap install chromium

  • Installing from default Ubuntu repositories

  • Using Puppeteer’s bundled Chromium (in our case)

All of these fail for the same reason:

Snap is fundamentally incompatible with standard Docker containers.


The Fix

The solution is to avoid Ubuntu’s Snap-based Chromium entirely and install Chromium from a non-Snap APT source that provides a real binary.

Once we did this:

  • Chromium installed correctly

  • /usr/bin/chromium existed as a real executable

  • Puppeteer launched successfully


Example Dockerfile

# Install Chromium from a non-Snap source
RUN add-apt-repository ppa:xtradeb/apps -y \
    && apt update \
    && apt install -y \
       chromium \
       fonts-liberation \
       libx11-xcb1 \
       libxcomposite1 \
       libxdamage1 \
       libxrandr2 \
       libgbm1 \
       libasound2 \
       libnspr4 \
       libnss3 \
       ca-certificates \
    && rm -rf /var/lib/apt/lists/*
Enter fullscreen mode Exit fullscreen mode

Puppeteer configuration:

ExecutablePath = "/usr/bin/chromium",
Args = new[]
{
    "--no-sandbox",
    "--disable-setuid-sandbox"
};
Enter fullscreen mode Exit fullscreen mode

Alternative Approaches (Tradeoffs)

There are a few ways to solve this problem:

  1. Use a non-Snap APT source (what we did)
*   Keeps Ubuntu base image

*   Requires managing external repo
Enter fullscreen mode Exit fullscreen mode
  1. Switch back to Debian-based images
*   Simpler dependency model

*   Diverge from official runtime images
Enter fullscreen mode Exit fullscreen mode
  1. Use Puppeteer’s bundled Chromium
*   Less setup

*   Larger image size, less control
Enter fullscreen mode Exit fullscreen mode

We chose option 1 to keep compatibility while maintaining control over dependencies.


Key Insight

This issue is not specific to Puppeteer or ASP.NET.

Any headless browser setup (Puppeteer, Playwright, Selenium) running inside Ubuntu-based Docker containers can hit this failure mode.

The underlying problem is a mismatch between:

  • Snap’s system-level assumptions

  • Docker’s minimal runtime model


Lessons Learned

  1. Base Images Are Part of Your Runtime Contract

    Framework upgrades can silently change your OS layer. Treat base images as a first-class dependency.

  2. “Installed” Does Not Mean “Runnable”

    Package managers can install indirections instead of binaries. Always verify the actual executable:

    file $(which chromium)
    
  3. Snap and Containers Don’t Mix

    Snap packages are not designed for containerized environments. Avoid them in production Docker setups.

  4. Validate Critical Dependencies Early

    If your system depends on external binaries (like browsers), validate them explicitly after upgrades—not after deployment failures.


Final Thoughts

This issue took longer to diagnose than expected because:

  • Installation appeared successful

  • Errors were misleading

  • The OS-level change wasn’t obvious

If you’re upgrading to ASP.NET 10 and rely on headless browsers, validate your Chromium setup early.

The failure mode is silent—and easy to miss.

Top comments (0)