DEV Community

IronSoftware
IronSoftware

Posted on

wkhtmltopdf Not Working on Alpine Linux: (Issue Fixed)

Deploying wkhtmltopdf on Alpine Linux consistently fails in ways that do not occur on Debian or Ubuntu distributions. Developers encounter cryptic errors about missing dynamic linkers, black squares instead of text, and library loading failures. The root cause is Alpine's use of musl libc instead of glibc, combined with wkhtmltopdf's binary dependencies on the GNU C library. With wkhtmltopdf archived since January 2023 and Alpine's popularity in containerized environments continuing to grow, this incompatibility affects a significant portion of Docker deployments.

The Problem

wkhtmltopdf is a command-line tool that converts HTML to PDF using the Qt WebKit rendering engine. The official wkhtmltopdf binaries and most prebuilt packages are compiled against glibc, the GNU C Library used by Debian, Ubuntu, CentOS, and most mainstream Linux distributions. Alpine Linux uses musl libc, a lightweight alternative that is not binary-compatible with glibc.

When developers attempt to run wkhtmltopdf on Alpine, the binary fails to execute because it cannot find the glibc dynamic linker. Even when using Alpine's packaged version of wkhtmltopdf (which is compiled against musl), developers encounter additional issues: missing font rendering libraries, X11 dependency failures, and black squares appearing instead of text in generated PDFs.

The combination of musl compatibility issues, missing dependencies, and Alpine's minimal base image creates a troubleshooting cycle where each fix reveals the next problem. Developers report spending hours or days attempting to make wkhtmltopdf functional on Alpine, only to encounter font rendering issues even after resolving library dependencies.

Error Messages and Symptoms

Dynamic Linker Missing (glibc binary on Alpine):

/lib64/ld-linux-x86-64.so.2: No such file or directory
Enter fullscreen mode Exit fullscreen mode
Error loading shared library ld-linux-x86-64.so.2: No such file or directory
Enter fullscreen mode Exit fullscreen mode

Library Loading Failures:

error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory
Enter fullscreen mode Exit fullscreen mode
error while loading shared libraries: libX11.so.6: cannot open shared object file: No such file or directory
Enter fullscreen mode Exit fullscreen mode
error while loading shared libraries: libXrender.so.1: cannot open shared object file: No such file or directory
Enter fullscreen mode Exit fullscreen mode
error while loading shared libraries: libXext.so.6: cannot open shared object file: No such file or directory
Enter fullscreen mode Exit fullscreen mode

Font and Rendering Issues:

QXcbConnection: Could not connect to display
Enter fullscreen mode Exit fullscreen mode
cannot load library Qt5Core: libicui18n.so.67: cannot open shared object file
Enter fullscreen mode Exit fullscreen mode

Visual Symptoms (no error message, but PDF is broken):

  • Black squares or rectangles instead of text
  • Completely blank pages in generated PDF
  • Some characters render while others appear as boxes
  • Fonts look different than expected or fall back to incorrect glyphs
  • PDF generation completes but output is unusable

Alpine 3.19+ Specific Issues:

wkhtmltopdf: error while loading shared libraries: libssl.so.1.1: cannot open shared object file
Enter fullscreen mode Exit fullscreen mode

Alpine 3.19 and later versions ship with OpenSSL 3.x, while many wkhtmltopdf builds expect OpenSSL 1.1.

Who Is Affected

This issue specifically impacts developers deploying wkhtmltopdf in Alpine-based environments.

Operating Systems: Alpine Linux (all versions), including Docker images based on alpine:*, node:*-alpine, python:*-alpine, and similar Alpine-based variants.

Container Platforms: Docker, Kubernetes, AWS ECS, Azure Container Instances, Google Cloud Run, and any orchestration platform where Alpine images are used to minimize container size.

Framework Wrappers: All wkhtmltopdf wrapper libraries attempting to run on Alpine, including Node.js packages (node-wkhtmltopdf, wkhtmltopdf-node), Python's pdfkit, Ruby's wicked_pdf and pdfkit gems, PHP's snappy/laravel-snappy, and .NET's DinkToPdf and Rotativa.

Use Cases: Invoice generation, report exports, HTML-to-PDF conversion pipelines, document archiving, and any automated PDF generation workflow deployed in Alpine containers.

Alpine's small image size (approximately 5MB base image versus 100MB+ for Debian slim) makes it attractive for containerized deployments, but this benefit is negated when attempting to install the dependencies required for wkhtmltopdf to function.

Evidence from the Developer Community

The wkhtmltopdf-on-Alpine issue generates consistent reports across Stack Overflow, GitHub issues, and developer forums. The combination of musl compatibility and missing dependencies has been documented repeatedly over several years.

Timeline

Date Event Source
2017-2018 Early reports of Alpine compatibility issues GitHub Issues
2019-05-01 Font rendering failures documented GitHub Issue #4401
2020-06-15 Alpine showing no characters, only squares GitHub Issue #4836
2021-03-01 Comprehensive dependency list documented Community blogs
2022-06-01 Alpine 3.16+ OpenSSL compatibility issues emerge GitHub Issues
2023-01-17 wkhtmltopdf project archived GitHub
2023-11-01 Alpine 3.19 released with OpenSSL 3.x, breaking older builds Alpine Linux
2024-2025 Ongoing reports with no upstream fixes available Various forums

Community Reports

"On Alpine Linux with wkhtmltopdf 0.12.6, no characters are rendered. I only see squares. After installing ttf-dejavu, ttf-droid, ttf-freefont, and ttf-liberation the PDF renders correctly."
-- Developer, GitHub Issue #4836

"I downloaded the Debian binary and got '/lib64/ld-linux-x86-64.so.2: No such file or directory'. Alpine uses musl, not glibc. You need to use the Alpine package or build from source."
-- Developer, Stack Overflow, 2020

"Even after installing all the packages, I still get black boxes instead of text. Had to add xvfb and run wkhtmltopdf under xvfb-run to get it working."
-- Developer, Reddit r/docker, 2021

"ttf-dejavu, ttf-droid, ttf-freefont, ttf-liberation are all font packages needed by wkhtmltopdf to render the pdf properly, and you might experience black boxes instead of letters if you are missing some."
-- Developer, wkhtmltopdf Alpine Docker setup guide

"Spent two days getting wkhtmltopdf to work on Alpine only to hit more issues in production. Should have just used Debian from the start."
-- Developer, GitHub Issues, 2022

Root Cause Analysis

The wkhtmltopdf Alpine Linux incompatibility stems from multiple architectural factors.

musl vs glibc Binary Incompatibility: Alpine Linux uses musl libc instead of the GNU C Library (glibc). These libraries are not binary compatible. Executables compiled against glibc expect the dynamic linker at /lib64/ld-linux-x86-64.so.2, which does not exist on Alpine. The musl dynamic linker is at a different path. This means glibc-compiled wkhtmltopdf binaries cannot run on Alpine without compatibility layers.

Alpine Package Limitations: While Alpine's package repository includes a musl-compiled wkhtmltopdf, this package has its own issues. It depends on Qt5 compiled for musl, which may behave differently from the glibc version. Font rendering, in particular, shows inconsistencies between the musl and glibc builds.

Missing Default Dependencies: Alpine's minimal philosophy means the base image lacks libraries that Debian includes by default. wkhtmltopdf requires:

  • libstdc++ - GNU C++ standard library
  • libx11 - X Window System client library
  • libxrender - X Rendering Extension
  • libxext - X11 extension library
  • libssl3 or libssl1.1 - OpenSSL (version depends on Alpine version)
  • ca-certificates - Certificate authority certificates for HTTPS
  • fontconfig - Font configuration and discovery
  • freetype - Font rendering engine
  • Font packages: ttf-droid, ttf-freefont, ttf-liberation, ttf-dejavu
  • xvfb - X Virtual Framebuffer (for headless operation)

Each missing dependency causes a different error, creating a troubleshooting chain.

Qt WebKit X11 Requirements: Even though wkhtmltopdf is described as "headless," the underlying Qt WebKit engine attempts to connect to an X display server. On Alpine, this requires either Xvfb (virtual framebuffer) or the QT_QPA_PLATFORM=offscreen environment variable, along with the associated libraries.

Font Rendering Stack: wkhtmltopdf relies on fontconfig to locate fonts and freetype to render them. Alpine's default image includes neither the configuration nor the fonts themselves. Without proper font packages, text renders as black squares because the renderer cannot find glyphs for the requested characters.

OpenSSL Version Mismatch: Alpine 3.19 and later ship with OpenSSL 3.x, while many wkhtmltopdf builds and dependencies expect OpenSSL 1.1.x. This causes library loading failures even after installing the main dependencies.

Archived Project: wkhtmltopdf was archived in January 2023. These Alpine-specific issues will never receive upstream fixes or improved musl compatibility.

Attempted Workarounds

Workaround 1: Install All Required Dependencies

Approach: Install the complete dependency tree in the Dockerfile.

FROM alpine:3.18

# Install wkhtmltopdf and all required dependencies
RUN apk add --no-cache \
    wkhtmltopdf \
    libstdc++ \
    libx11 \
    libxrender \
    libxext \
    libssl3 \
    ca-certificates \
    fontconfig \
    freetype \
    ttf-droid \
    ttf-freefont \
    ttf-liberation \
    ttf-dejavu \
    xvfb \
    && fc-cache -f

# Set environment variable for headless Qt
ENV QT_QPA_PLATFORM=offscreen

# Create wrapper script to run with virtual framebuffer
RUN echo '#!/bin/sh' > /usr/local/bin/wkhtmltopdf-wrapper && \
    echo 'xvfb-run -a --server-args="-screen 0 1024x768x24" wkhtmltopdf "$@"' >> /usr/local/bin/wkhtmltopdf-wrapper && \
    chmod +x /usr/local/bin/wkhtmltopdf-wrapper
Enter fullscreen mode Exit fullscreen mode

Limitations:

  • Significantly increases image size, negating Alpine's primary benefit
  • Requires xvfb-run wrapper for reliable operation
  • Does not work on Alpine 3.19+ without additional OpenSSL workarounds
  • Font rendering may still differ from Debian-based systems
  • Security vulnerabilities in archived wkhtmltopdf remain unpatched

Workaround 2: Use glibc Compatibility Layer

Approach: Install glibc compatibility package to run glibc-compiled wkhtmltopdf.

FROM alpine:3.18

# Install glibc compatibility layer
RUN apk add --no-cache wget && \
    wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \
    wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.35-r1/glibc-2.35-r1.apk && \
    apk add --no-cache --force-overwrite glibc-2.35-r1.apk && \
    rm glibc-2.35-r1.apk

# Download glibc-compiled wkhtmltopdf
RUN wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox-0.12.6.1-3.linux-generic-amd64.tar.xz && \
    tar -xf wkhtmltox-0.12.6.1-3.linux-generic-amd64.tar.xz && \
    mv wkhtmltox/bin/* /usr/local/bin/ && \
    rm -rf wkhtmltox*
Enter fullscreen mode Exit fullscreen mode

Limitations:

  • glibc compatibility packages are community-maintained, not official
  • Mixing musl and glibc can cause unpredictable behavior
  • Does not solve font or X11 dependency issues
  • Increases complexity and attack surface
  • Third-party package sources may have security implications

Workaround 3: Alpine 3.19+ OpenSSL Compatibility

Approach: Install OpenSSL 1.1 compatibility package for newer Alpine versions.

FROM alpine:3.19

# Add edge repository for OpenSSL 1.1 compatibility
RUN echo "https://dl-cdn.alpinelinux.org/alpine/v3.16/community" >> /etc/apk/repositories

# Install with OpenSSL 1.1 from older repository
RUN apk add --no-cache \
    wkhtmltopdf \
    libssl1.1 \
    # ... other dependencies
Enter fullscreen mode Exit fullscreen mode

Alternative approach using symbolic links (not recommended for production):

# Create symlinks from OpenSSL 3 to expected OpenSSL 1.1 paths
RUN ln -s /usr/lib/libssl.so.3 /usr/lib/libssl.so.1.1 && \
    ln -s /usr/lib/libcrypto.so.3 /usr/lib/libcrypto.so.1.1
Enter fullscreen mode Exit fullscreen mode

Limitations:

  • Mixing repository versions can cause package conflicts
  • Symbolic link approach may cause runtime errors due to ABI differences
  • OpenSSL 1.1 is deprecated and has known vulnerabilities
  • Adds maintenance burden tracking multiple Alpine versions

Workaround 4: Switch to Debian-Based Image

Approach: Abandon Alpine and use Debian slim for wkhtmltopdf workloads.

# Replace Alpine with Debian slim
FROM debian:bookworm-slim

RUN apt-get update && apt-get install -y \
    wkhtmltopdf \
    xvfb \
    fonts-liberation \
    fonts-dejavu-core \
    && rm -rf /var/lib/apt/lists/*
Enter fullscreen mode Exit fullscreen mode

Limitations:

  • Increases image size from ~5MB (Alpine) to ~80MB (Debian slim)
  • Requires changing base image, potentially affecting other services
  • Still requires xvfb and font packages
  • Does not address wkhtmltopdf's archived status or security issues

Workaround 5: Environment Variables for Headless Mode

Approach: Set Qt environment variables to avoid X11 connection attempts.

ENV QT_QPA_PLATFORM=offscreen
ENV DISPLAY=:99
Enter fullscreen mode Exit fullscreen mode
# Run with environment variables
QT_QPA_PLATFORM=offscreen wkhtmltopdf input.html output.pdf
Enter fullscreen mode Exit fullscreen mode

Limitations:

  • Does not work reliably in all scenarios
  • Some Qt operations still require X11 libraries even with offscreen platform
  • Must be combined with font and library installations
  • Behavior varies between wkhtmltopdf versions

A Different Approach: IronPDF

For containerized deployments where Alpine Linux is preferred or required, IronPDF provides an alternative architecture that does not depend on musl/glibc compatibility, X11 libraries, or system font configuration. IronPDF embeds a Chromium rendering engine designed for headless server environments, including native support for Alpine Linux.

Why IronPDF Works on Alpine Without These Issues

IronPDF's architecture fundamentally differs from wkhtmltopdf:

  1. Native Alpine Support: IronPDF provides Alpine-compatible packages that run on musl libc without glibc compatibility layers
  2. No X11 Dependency: The embedded Chromium engine runs in true headless mode without requiring Xvfb or X11 libraries
  3. Embedded Font Handling: Font rendering is handled by Chromium's internal systems, not system fontconfig
  4. No Qt WebKit: Uses modern Chromium instead of abandoned Qt WebKit engine
  5. Active Maintenance: Regular updates address compatibility with new Alpine versions including OpenSSL changes

Code Example

using IronPdf;
using System;

/// <summary>
/// Demonstrates HTML-to-PDF conversion on Alpine Linux without wkhtmltopdf.
/// No musl compatibility issues, X11, xvfb, or font packages required.
/// </summary>
public class AlpinePdfGenerator
{
    public void GeneratePdf()
    {
        // Configure for Linux environments - auto-detects Alpine
        Installation.LinuxAndDockerDependenciesAutoConfig = true;

        // Create the renderer - uses embedded Chromium
        var renderer = new ChromePdfRenderer();

        // Configure rendering options
        renderer.RenderingOptions.PaperSize = IronPdf.Rendering.PdfPaperSize.A4;
        renderer.RenderingOptions.MarginTop = 20;
        renderer.RenderingOptions.MarginBottom = 20;
        renderer.RenderingOptions.MarginLeft = 15;
        renderer.RenderingOptions.MarginRight = 15;

        // HTML with CSS, fonts, and content that fails on wkhtmltopdf Alpine
        string html = @"
<!DOCTYPE html>
<html>
<head>
    <meta charset='UTF-8'>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');

        body {
            font-family: 'Roboto', sans-serif;
            margin: 40px;
            color: #333;
        }

        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px;
            border-radius: 8px;
            margin-bottom: 30px;
        }

        table {
            width: 100%;
            border-collapse: collapse;
            margin: 20px 0;
        }

        th, td {
            border: 1px solid #ddd;
            padding: 12px;
            text-align: left;
        }

        th {
            background-color: #f8f9fa;
            font-weight: 700;
        }

        .currency {
            font-size: 18px;
        }

        .multilingual {
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <div class='header'>
        <h1>Invoice #12345</h1>
        <p>Generated on Alpine Linux container</p>
    </div>

    <table>
        <tr><th>Item</th><th>Quantity</th><th>Price</th></tr>
        <tr><td>Widget A</td><td>10</td><td>$100.00</td></tr>
        <tr><td>Widget B</td><td>5</td><td>$75.00</td></tr>
        <tr><td>Service Fee</td><td>1</td><td>$25.00</td></tr>
    </table>

    <p class='currency'>Currency symbols: $ £ € ¥ ₹ ₽</p>

    <div class='multilingual'>
        <p><strong>Multilingual support:</strong></p>
        <p>Chinese: 你好世界</p>
        <p>Japanese: こんにちは</p>
        <p>Korean: 안녕하세요</p>
        <p>Arabic: مرحبا</p>
        <p>Greek: Γειά σου κόσμε</p>
    </div>
</body>
</html>";

        // Render to PDF - works on Alpine without musl/glibc issues
        using (var pdf = renderer.RenderHtmlAsPdf(html))
        {
            pdf.SaveAs("/app/output/invoice.pdf");
            Console.WriteLine($"PDF generated successfully: {pdf.BinaryData.Length} bytes");
        }
    }

    public void ConvertUrlToPdf(string url)
    {
        var renderer = new ChromePdfRenderer();

        // Wait for JavaScript to complete
        renderer.RenderingOptions.WaitFor.JavaScript(2000);

        // Render web page to PDF
        using (var pdf = renderer.RenderUrlAsPdf(url))
        {
            pdf.SaveAs("/app/output/webpage.pdf");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Dockerfile for IronPDF on Alpine:

FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src
COPY ["YourApp.csproj", "./"]
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine
WORKDIR /app

# Minimal dependencies for IronPDF on Alpine
# NO xvfb, X11, fontconfig, or font packages required
RUN apk add --no-cache \
    icu-libs \
    krb5-libs \
    libgcc \
    libintl \
    libssl3 \
    libstdc++ \
    zlib

COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "YourApp.dll"]
Enter fullscreen mode Exit fullscreen mode

Comparison: wkhtmltopdf vs IronPDF on Alpine Linux

Requirement wkhtmltopdf IronPDF
musl/glibc compatibility Problematic Native support
X11 / libXrender Required Not needed
xvfb Required Not needed
fontconfig Required Not needed
ttf-* font packages Required Not needed
OpenSSL version issues Yes (3.19+) Handled internally
Image size overhead ~100MB+ added ~30MB added
Wrapper scripts Required Not needed

Key points about this code:

  • LinuxAndDockerDependenciesAutoConfig automatically detects Alpine and configures appropriately
  • No musl vs glibc compatibility issues because IronPDF provides native Alpine builds
  • No xvfb-run wrapper or QT_QPA_PLATFORM environment variable required
  • Google Fonts and web fonts load automatically without system font installation
  • Unicode characters render using Chromium's built-in font fallback
  • Consistent output between development machines and Alpine production containers

API Reference

For more details on Alpine and Linux deployment:

Migration Considerations

Licensing

IronPDF is commercial software with per-developer licensing. A free trial is available for evaluation. wkhtmltopdf is open source under LGPLv3. The licensing cost should be weighed against the engineering time spent troubleshooting musl compatibility, maintaining complex Dockerfiles, and addressing security vulnerabilities in archived software.

API Differences

Migration from wkhtmltopdf wrapper libraries involves API changes:

Command-line equivalent mapping:

wkhtmltopdf Flag IronPDF Equivalent
--page-size A4 PaperSize = PdfPaperSize.A4
--margin-top 20mm MarginTop = 20
--javascript-delay 2000 WaitFor.JavaScript(2000)
--print-media-type CssMediaType = PdfCssMediaType.Print
--no-background PrintHtmlBackgrounds = false
--orientation Landscape PaperOrientation = PdfPaperOrientation.Landscape

Wrapper library migration:

# Before: Python pdfkit on Alpine (fails without extensive setup)
import pdfkit
pdfkit.from_string('<h1>Hello</h1>', 'output.pdf')

# After: IronPDF Python package
from ironpdf import PdfDocument
pdf = PdfDocument.render_html_as_pdf('<h1>Hello</h1>')
pdf.save_as('output.pdf')
Enter fullscreen mode Exit fullscreen mode

What You Gain

  • Native Alpine Linux support without musl/glibc workarounds
  • No X11, xvfb, fontconfig, or font package dependencies
  • Smaller Docker images compared to wkhtmltopdf with all dependencies
  • Modern CSS and JavaScript support via Chromium engine
  • Active maintenance and security updates
  • Consistent rendering across development and production

What to Consider

  • Commercial licensing cost
  • Chromium engine has a larger memory footprint than Qt WebKit
  • API migration effort for existing wkhtmltopdf integrations
  • Different rendering engine may produce slightly different output
  • Learning curve for teams familiar with wkhtmltopdf configuration

Conclusion

wkhtmltopdf on Alpine Linux fails due to fundamental incompatibilities between musl libc and glibc-compiled binaries, compounded by missing X11 and font dependencies. The workarounds required to make wkhtmltopdf functional on Alpine are extensive, fragile, and negate Alpine's primary benefit of minimal image size. With wkhtmltopdf archived since January 2023, these issues will never receive upstream fixes. For teams committed to Alpine-based deployments, using a library with native musl support and no X11 dependencies eliminates the compatibility issues rather than working around them.


Jacob Mellor built IronPDF and leads the technical team at Iron Software with over 25 years of commercial software experience.


References

  1. wkhtmltopdf GitHub Repository - Archived{:rel="nofollow"} - Official repository, archived January 2023
  2. No characters rendered on Alpine - GitHub Issue #4836{:rel="nofollow"} - Alpine showing only black squares
  3. Setting up WKHTMLTOPDF on Docker Alpine Linux{:rel="nofollow"} - Community guide on Alpine dependencies
  4. musl libc - Differences from glibc{:rel="nofollow"} - Technical documentation on musl/glibc differences
  5. Alpine Linux - musl libc{:rel="nofollow"} - Alpine's official musl documentation
  6. wkhtmltopdf Status Page{:rel="nofollow"} - Official project status and known issues
  7. Stack Overflow - wkhtmltopdf Alpine questions{:rel="nofollow"} - Community questions about Alpine deployment

For IronPDF documentation and tutorials, visit ironpdf.com.

Top comments (0)