PDFPipe

Managed containers / Google Cloud Run

WeasyPrint on Google Cloud Run: a small image that renders differently

The deploy is easy, the memory footprint is a tenth of a browser's, and the documents come out subtly wrong: a chart is missing, a web font fell back, a flex layout collapsed.

What is actually wrong

This is the one combination on this list where the infrastructure is not the problem. WeasyPrint installs from apt in a few lines and runs happily in a small container. What changes is the renderer: it implements CSS Paged Media rather than a browser's layout engine, it does not run JavaScript at all, and its support for newer layout features is not a browser's.

What Google Cloud Run gives you to work with

your container image, started on demand and scaled to zero, with requests routed into it.

  • Packaging: no meaningful image size limit. Image size shows up as cold start latency instead, because the image has to be pulled before the first request is served.
  • Filesystem: the container filesystem is writable, but writes go to memory and count against the instance memory limit. A process that writes a large temporary file is really allocating memory, and it will be killed for it.
  • Processes: full process control inside the container. The default request concurrency is high, which means many simultaneous renders inside one instance rather than one per instance.

The fix, if you are keeping WeasyPrint

Install the system libraries in the image and accept the trade deliberately rather than discovering it in production. Anything drawn by client-side JavaScript has to be rendered server-side into the HTML before WeasyPrint sees it, and web fonts have to be present as files rather than fetched.

dockerfile
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
      libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b \
      libcairo2 libgdk-pixbuf-2.0-0 fonts-dejavu-core \
 && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir weasyprint

What still hurts after that

The version of Pango in the base image and the version WeasyPrint expects drift apart between releases, and the symptom of a mismatch is bad text shaping rather than an error. Fonts have to be installed as system packages and referenced by family name, so a design using a web font either ships that font in the image or does not get it. In exchange you get the page margin boxes that browser engines never implemented, which is a real capability and the reason to choose this on purpose.

What WeasyPrint is genuinely good at

it implements the parts of the paged media specification that browser engines never did, including running headers and footers in page margin boxes, and it uses a fraction of the memory a browser does. That is worth keeping in view: the problem on this page is where it runs, not what it produces. WeasyPrint is a Python library that implements CSS Paged Media directly, without a browser, on top of the Pango and Cairo text and graphics stack. It needs Pango, Cairo, HarfBuzz, GDK-PixBuf and GLib as system shared libraries. These are not Python packages and no wheel contains them.

The other shape of the answer

Every fix above keeps the renderer inside your deployment, which means the constraint stays yours: the package size, the shared libraries, the fonts, the memory, the patching. The alternative is an HTTP call from Google Cloud Run to something that already has a browser in it, which turns all of the above into a request and a response. That is what this API is, and it is the reason these pages exist. Whether that trade is right depends on whether rendering is a thing you want to operate.

Frequently asked

Can WeasyPrint run on Google Cloud Run at all?

Install the system libraries in the image and accept the trade deliberately rather than discovering it in production. Anything drawn by client-side JavaScript has to be rendered server-side into the HTML before WeasyPrint sees it, and web fonts have to be present as files rather than fetched. Whether that is worth doing is a separate question from whether it is possible, and the section above on what still hurts is the honest answer to it.

Why does it work locally?

Because a development machine has root, a package manager, several gigabytes of memory, a full font set and no packaging limit. Every one of those is a thing Google Cloud Run constrains. A renderer that works locally and fails on deploy has not regressed: it has met the first environment that does not give it everything.

Related failures

The same platform with a different renderer, and the same renderer on other platforms.

If the render moves off this platform, none of the above is your problem to keep solving. 100 free documents a month, and a playground that needs no signup.