Fix WeasyPrint "Cannot Load Library" for Django PDF Generation in Docker

Fix WeasyPrint OSError cannot load library gobject-2.0-0, libpango, or libharfbuzz when generating PDFs in Django on Docker, Heroku, or a slim server: install Pango and fonts, use the right package names, and keep PDF rendering in a worker.

Problem statement

Invoice or report PDFs work locally, but in production the view crashes:

OSError: cannot load library 'gobject-2.0-0': gobject-2.0-0: cannot open shared object
file: No such file or directory. Additionally, ctypes.util.find_library() did not
manage to locate a library called 'gobject-2.0-0'

or names libpango-1.0-0, libharfbuzz-0, or libfontconfig. WeasyPrint is a Python package, but it renders text and layout with Pango, HarfBuzz, and fontconfig, which are C libraries from the operating system. Slim Docker images and minimal servers do not include them, and pip install weasyprint cannot install them.

Why PDF rendering failsView renders PDFWeasyPrintLoads Pangovia ffiLibrary missingslim imageOSErrorcannot load libraryWeasyPrint needs Pango and its dependencies from the OS, not from pip.
  1. Debian or Ubuntu image: apt-get install -y libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz0b libfontconfig1.
  2. Add fonts: fonts-dejavu-core or the fonts your templates use; without them PDFs render with boxes or fallback fonts.
  3. On Alpine: apk add pango fontconfig ttf-dejavu, and expect larger effort than on Debian.
  4. On Heroku, add the system libraries with an Apt buildpack or deploy a Docker image.
  5. Generate large PDFs in a Celery task, not in the request.

Step-by-step solution

1. Install the system libraries

For python:3.12-slim (Debian):

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 libharfbuzz-subset0 \
       libfontconfig1 fonts-dejavu-core \
    && rm -rf /var/lib/apt/lists/*

WeasyPrint's documentation lists the exact packages per distribution and version; recent versions no longer need Cairo or GDK-Pixbuf. Check with:

docker run --rm myapp python -c "import weasyprint; print(weasyprint.__version__)"

Importing WeasyPrint loads the libraries, so a successful import confirms the fix.

layers under the Python packageweasyprint (pip)HTML and CSS layoutPango and HarfBuzztext shaping, aptfontconfigfinds fonts, aptFont filesDejaVu or your brand fontsOnly the top layer comes from pipdjango-deployment.com
Missing fonts do not raise errors; they produce PDFs with the wrong typeface.

2. Add the fonts your templates use

If your CSS asks for font-family: Inter, the font must be installed in the image or loaded with @font-face from a file WeasyPrint can read. Copy fonts into the image and refresh the cache:

COPY fonts/ /usr/share/fonts/truetype/app/
RUN fc-cache -f

Or reference them in the PDF stylesheet with an absolute file:// URL or through Django static files using a base_url when rendering:

HTML(string=html, base_url=request.build_absolute_uri("/")).write_pdf()

3. Handle Heroku and other buildpack platforms

Heroku's stack images include some libraries, but versions and availability change between stacks. The reliable options are the heroku-community/apt buildpack with an Aptfile listing the packages, or a container deploy with your own Dockerfile.

4. Keep rendering out of the request

PDF rendering is CPU-heavy and can take seconds for multi-page documents. Long renders tie up a Gunicorn worker and may hit timeouts. Render in a Celery task, store the file in media storage, and send or show a link:

@shared_task
def render_invoice_pdf(invoice_id):
    invoice = Invoice.objects.get(pk=invoice_id)
    html = render_to_string("invoices/pdf.html", {"invoice": invoice})
    pdf = HTML(string=html, base_url=settings.SITE_URL).write_pdf()
    invoice.pdf.save(f"invoice-{invoice.number}.pdf", ContentFile(pdf))

5. Check memory

Large documents and images use a lot of memory during layout. Workers that render PDFs may need more memory than web workers; run them as a separate Celery queue with fewer concurrent tasks.

symptom and fixSymptomCause and fixcannot load gobject or pangolibraries missing;apt-get install pangoboxes instead of lettersno fonts; installDejaVu or brand fontsimages or CSS missingrelative URLs; pass base_urlrequest times outslow render; move to a Celery taskLibrary errors fail loudly; font and URL problems fail quietlydjango-deployment.com
Compare a production PDF with a local one after every image change.

Explanation

WeasyPrint calls Pango and HarfBuzz through cffi, loading them by library name at import time. If dlopen cannot find them, Python raises OSError: cannot load library, naming the first one it tried, often gobject-2.0-0 because GLib is a dependency of Pango. Development machines typically have these through a desktop environment or Homebrew; containers and servers built for size do not. Fonts are found through fontconfig at render time, and a missing font silently falls back to whatever is available.

Edge cases / notes

  • Old WeasyPrint versions. Versions before 53 used Cairo and needed more libraries. Upgrading simplifies dependencies.
  • Alternative tools. Headless Chromium (via Playwright) renders PDFs too and needs its own large set of libraries.
  • Security. Do not render user-supplied HTML with network access; a url_fetcher that blocks external URLs prevents server-side request forgery.

Next steps

FAQ

Why does pip install weasyprint not fix it?

Pip installs the Python package only. Pango, HarfBuzz, and fontconfig are system libraries installed with apt or apk.

Which fonts should I install?

The ones your PDF CSS uses, plus a fallback such as DejaVu. Copy brand fonts into the image.

Does WeasyPrint work on Alpine?

Yes, with pango and fonts installed via apk, but Debian-based images are usually simpler.

Should PDFs be generated in the view?

Small single-page PDFs can be. Larger documents belong in a background task.

Keep going