Table of Contents

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:

  1. Picks the right variant for the host's glibc — u2404 (newest WebKit, glibc ≥ 2.38) or u2204 (broad compatibility, glibc ≥ 2.35).
  2. Downloads the tarball from the public release feed (override below), printing a one-line console notice so first launch is never a silent hang.
  3. 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:

  1. PoolOptions.BundleCacheDirectory
  2. COBALT_BUNDLE_CACHE_DIR environment variable
  3. /home/site/cobaltbundle when /home/site exists (Azure App Service marker)
  4. $XDG_CACHE_HOME/CobaltPdfWebKit, then ~/.cache/CobaltPdfWebKit
  5. /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.