Serverless functions / AWS Lambda
wkhtmltopdf on AWS Lambda: a binary with no libraries to link against
"error while loading shared libraries: libXrender.so.1: cannot open shared object file", or the process exits with no output and no error at all.
What is actually wrong
wkhtmltopdf is a Qt application. Even the headless build links against X11 rendering and font libraries, and the Lambda base image does not ship them, because nothing else on a serverless runtime needs an X server's client libraries. The binary is present, executable, and cannot start.
What AWS Lambda gives you to work with
a function that is handed a request, runs, and is frozen again. The execution environment is reused between invocations but you do not control when it is torn down.
- Packaging: 250 MB unzipped for the function code and every layer attached to it, combined. That is the hard number most PDF deployments run into. A container image raises it to 10 GB but changes how the function is built and pushed.
- Filesystem: the deployment package is read-only. Only /tmp is writable, and it defaults to 512 MB. Anything the process writes there survives for the life of the execution environment, not the invocation, which is a subtle source of both caching wins and stale-state bugs.
- Processes: a child process can be spawned and it lives while the invocation does. When the invocation returns, the environment is frozen mid-execution rather than terminated, so a process that has not exited is simply suspended and resumed on the next call.
The fix, if you are keeping wkhtmltopdf
Build a layer on the same base image Lambda runs, copy in the binary along with every shared object it resolves, and point the dynamic linker at them. Then add fonts, because wkhtmltopdf ships none and the base image has none either.
# In a container built FROM the Lambda base image, so the shared
# objects match the runtime that will load them.
ldd /usr/local/bin/wkhtmltopdf # every line here has to come with you
# Layer layout: /opt/bin/wkhtmltopdf, /opt/lib/*.so, /opt/fonts/*
# Then, on the function:
# LD_LIBRARY_PATH=/opt/lib:$LD_LIBRARY_PATH
# FONTCONFIG_PATH=/opt/fontsWhat still hurts after that
Without a fontconfig cache every render rebuilds the font list, which is slow on a cold start and produces empty boxes instead of text if it fails. The layer is compiled against one base image, so an AWS runtime upgrade breaks it and the breakage looks like a random rendering fault. Underneath all of it, the engine is a fork of WebKit that stopped tracking the web a long time ago, so flexbox and grid layouts do not render the way the browser you designed them in does.
What wkhtmltopdf is genuinely good at
it is a single binary with no runtime, so once it is present it does not care what language your application is written in. That is worth keeping in view: the problem on this page is where it runs, not what it produces. wkhtmltopdf is a command line tool built on an old fork of the Qt WebKit engine, invoked as a subprocess with an HTML file and an output path. It needs the binary itself, the X11 and font shared libraries it links against even in headless mode, and a font configuration, because it ships with no fonts of its own.
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 AWS Lambda 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 wkhtmltopdf run on AWS Lambda at all?
Build a layer on the same base image Lambda runs, copy in the binary along with every shared object it resolves, and point the dynamic linker at them. Then add fonts, because wkhtmltopdf ships none and the base image has none either. 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 AWS Lambda 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.
Puppeteer on AWS Lambda: the 250 MB package limit
The deploy fails before the function ever runs, with an error naming the unzipped size of the code and layers.
Playwright on AWS Lambda: browsers that are not in your bundle
At runtime, not at deploy time: "Executable doesn't exist at /home/sbx_user1051/.cache/ms-playwright/chromium-.../chrome-linux/chrome".
WeasyPrint on AWS Lambda: the libraries pip cannot install
"OSError: cannot load library 'libgobject-2.0-0': libgobject-2.0-0: cannot open shared object file", raised on import rather than on render.
wkhtmltopdf on Docker: the apt package is the wrong build
It installs cleanly, it renders, and the header, the footer and the page numbers are silently missing.
wkhtmltopdf on Kubernetes: processes that never exit
Over hours or days the pod slows down and eventually stops responding. Process listings show a growing number of wkhtmltopdf processes in a defunct state.
wkhtmltopdf on Heroku: a binary with nowhere to live
Locally it works because the tool is installed on your machine. On a dyno, the command is not found.
wkhtmltopdf on Railway: an apt package the build never runs
Command not found, at runtime, on a build whose logs show no errors at all.
Every renderer, on every platform
The full grid, grouped by what kind of environment the platform is.
What goes wrong when HTML becomes a PDF
Once it deploys, the next set of problems is in the output rather than the infrastructure.
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.