The Render Bundle
CobaltPDF.WebKit's engine ships as a self-contained Linux bundle: WebKitGTK, GTK,
fontconfig, fonts, a Python runtime, Xvfb, Mesa software GL, and every transitive shared
library — everything except glibc, which comes from the host. That is why deployments
need no system packages and no container.
| Download size | ~262 MB (gzip tarball) |
| Extracted size | ~611 MB |
| Host requirement | x64 Linux, glibc ≥ 2.35 |
| Integrity | SHA-256 pinned in the library, verified on every download |
Automatic provisioning
On the first render (or at CobaltEngine.PreWarmAsync()), the library:
- Picks the right variant for the host's glibc —
u2404(newest WebKit, glibc ≥ 2.38) oru2204(broad compatibility, glibc ≥ 2.35). - Downloads the tarball from the public release feed (override below), printing a one-line console notice so first launch is never a silent hang.
- Verifies the pinned SHA-256, extracts to the cache directory, and marks it ready.
Subsequent starts find the cache and skip everything — warm start is microseconds. A tarball left by an interrupted attempt is hash-verified and reused rather than re-downloaded.
Cache location
First match wins:
PoolOptions.BundleCacheDirectoryCOBALT_BUNDLE_CACHE_DIRenvironment variable/home/site/cobaltbundlewhen/home/siteexists (Azure App Service marker)$XDG_CACHE_HOME/CobaltPdfWebKit, then~/.cache/CobaltPdfWebKit/tmp/CobaltPdfWebKit
Important
On Azure App Service / Functions, always set COBALT_BUNDLE_CACHE_DIR=/tmp/cobaltbundle.
The /home default is an Azure Files network share — extracting ~2,000 files over CIFS
is 10–100× slower than local disk and the first render can time out. /tmp is instance-local:
each new instance re-downloads once (~40 s), then caches for its lifetime.
Progress and logging
CobaltEngine.Configure(o =>
{
o.OnBundleDownloadProgress = p => logger.LogInformation(
"bundle {Stage}: {Done}/{Total} bytes", p.Stage, p.BytesReceived, p.TotalBytes);
o.QuietBundleDownload = true; // suppress the console notice in production logs
});
Pre-staging (faster cold starts, Docker images)
Place the tarball in the cache directory ahead of time — the library verifies its hash and goes straight to extraction, skipping the download:
ENV COBALT_BUNDLE_CACHE_DIR=/opt/cobalt-bundle
ADD https://github.com/CobaltPDF/cobalt-webkit-bundles/releases/download/v1.0.0/cobalt-webkit-bundle-linux-x64-glibc-u2204.tar.gz \
/opt/cobalt-bundle/cobalt-webkit-bundle-linux-x64-glibc-u2204-v1.0.0.tar.gz
Note
The staged file name embeds the bundle version: cobalt-webkit-bundle-{rid}-{variant}-v{version}.tar.gz.
Pick u2204 for glibc 2.35–2.37 bases (Debian 12, Ubuntu 22.04) or u2404 for
glibc ≥ 2.38 (Ubuntu 24.04).
Air-gapped and self-hosted feeds
Host the tarballs on your own server (Artifactory, S3, a file share) and point the library at it:
| Setting | Env var | Purpose |
|---|---|---|
PoolOptions.BundleBaseUrl |
COBALT_BUNDLE_BASE_URL |
Base URL; final URL is {base}/v{version}/cobalt-webkit-bundle-{rid}-{variant}.tar.gz |
PoolOptions.BundleSha256Override |
COBALT_BUNDLE_SHA256 |
Expected hash when self-hosting a modified bundle |
PoolOptions.AutoDownloadBundle = false |
— | Hard-fail instead of downloading — surfaces any unexpected outbound call |
Outbound HTTPS for the default feed goes to github.com and
release-assets.githubusercontent.com — allow these in egress-locked VNets, or self-host.
Bundle version vs package version
The bundle is versioned independently of the NuGet package and the cache path embeds the bundle version, so upgrading a package that ships a newer bundle never collides with an older extraction — instances cleanly download the new version on first start.