Developers deploying QuestPDF encounter "Unable to load shared library 'QuestPdfSkia'" errors, particularly on IIS, Docker, and Linux environments. The library's native SkiaSharp dependencies are not always correctly resolved at runtime, causing DllNotFoundException that blocks PDF generation. This article documents the common causes and explores deployment alternatives.
The Problem
QuestPDF uses SkiaSharp for rendering, which requires native platform-specific libraries. These libraries must be correctly deployed and accessible at runtime. When they're not found, QuestPDF throws:
System.DllNotFoundException: Unable to load shared library 'QuestPdfSkia' or one of its dependencies
The issue occurs because:
- Native libraries are not copied to the output directory
- Platform-specific NuGet packages are not installed
- Runtime environment cannot find the library path
- IIS application pools have restricted access
Error Messages and Symptoms
Full error message:
QuestPDF.Drawing.Exceptions.InitializationException: Cannot create the PDF document using the SkiaSharp-related library.
---> System.DllNotFoundException: Unable to load shared library 'QuestPdfSkia' or one of its dependencies.
On IIS specifically:
QuestPdfSkia.dll not found on IIS on .NET Framework
Symptoms include:
- Application works in Visual Studio but fails when deployed to IIS
- Works in development but fails in Docker containers
- Works on Windows but fails on Linux servers
- Inconsistent behavior between environments
Who Is Affected
This issue impacts production deployments across platforms:
Hosting Environments: IIS, Azure App Service, Docker containers, Kubernetes, Linux servers.
Framework Versions: .NET Framework 4.x on IIS, .NET Core/.NET 5-8 on various platforms.
QuestPDF Versions: Multiple versions affected, including recent releases.
Evidence from the Developer Community
GitHub Issues
| Issue | Title | Activity |
|---|---|---|
| #1097 | QuestPdfSkia.dll not found on IIS on .NET Framework | 2024 |
| #812 | Unable to load shared library 'QuestPdfSkia' or one of its dependencies | 2024 |
| #667 | Docs are stuck in 1990's - need AWS or Azure example | 2023-2025 |
Developer Reports
"QuestPdfSkia.dll not found on IIS on .NET Framework. Works fine in Visual Studio."
— Developer, GitHub Issue #1097"Unable to load shared library 'QuestPdfSkia' or one of its dependencies when deploying to Linux container."
— Developer, GitHub Issue #812
Root Cause Analysis
Several factors contribute to the DLL not found errors:
NuGet Package Resolution: The SkiaSharp.NativeAssets packages may not be correctly referenced or may not match the runtime platform
Build Output: Native DLLs may not be copied to the build output directory
IIS Application Pool Identity: The IIS worker process may not have permission to load native libraries
PATH Environment: The library search path may not include the directory containing native DLLs
Platform Mismatch: x86 vs x64 mismatch between the application and native libraries
Solution Matrix by Deployment Scenario
The following matrix provides the specific solution for each combination of hosting environment and operating system:
| Scenario | OS | Fix | NuGet Packages Required |
|---|---|---|---|
| IIS (.NET Framework) | Windows x64 | Set pool to 64-bit, add native packages | SkiaSharp.NativeAssets.Win32 |
| IIS (.NET Core/.NET 5+) | Windows x64 | Publish as self-contained | Built-in |
| Azure App Service | Linux | Add Linux native packages | SkiaSharp.NativeAssets.Linux.NoDependencies |
| Docker (Debian) | Linux | Install system dependencies | SkiaSharp.NativeAssets.Linux.NoDependencies |
| Docker (Alpine) | Linux | Use musl-compatible packages | SkiaSharp.NativeAssets.Linux.NoDependencies |
| AWS Lambda | Linux | Custom runtime layer | SkiaSharp.NativeAssets.Linux.NoDependencies |
| Google Cloud Run | Linux | Include in container | SkiaSharp.NativeAssets.Linux.NoDependencies |
| Self-hosted Linux | Debian/Ubuntu | Install libfontconfig | SkiaSharp.NativeAssets.Linux |
| Self-hosted Linux | CentOS/RHEL | Install additional deps | SkiaSharp.NativeAssets.Linux.NoDependencies |
Complete .csproj Examples
For Windows IIS Deployment (.NET 8):
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<!-- Ensure 64-bit output -->
<PlatformTarget>x64</PlatformTarget>
<!-- Self-contained deployment includes all dependencies -->
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuestPDF" Version="2024.10.2" />
<!-- Explicitly include for .NET Framework compatibility -->
<PackageReference Include="SkiaSharp.NativeAssets.Win32" Version="2.88.7" />
<PackageReference Include="HarfBuzzSharp.NativeAssets.Win32" Version="7.3.0" />
</ItemGroup>
</Project>
For Linux Container Deployment:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<SelfContained>false</SelfContained>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="QuestPDF" Version="2024.10.2" />
<!-- NoDependencies variant doesn't require system libraries -->
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.7" />
<PackageReference Include="HarfBuzzSharp.NativeAssets.Linux" Version="7.3.0" />
</ItemGroup>
</Project>
For AWS Lambda:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<AWSProjectType>Lambda</AWSProjectType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Amazon.Lambda.Core" Version="2.2.0" />
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.4.0" />
<PackageReference Include="QuestPDF" Version="2024.10.2" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.7" />
<PackageReference Include="HarfBuzzSharp.NativeAssets.Linux" Version="7.3.0" />
</ItemGroup>
</Project>
IIS Configuration Guide
Step 1: Configure Application Pool
In IIS Manager:
- Select Application Pools in the left panel
- Right-click your application pool and select "Advanced Settings"
- Under "General" section, set Enable 32-Bit Applications to False
- Under "Process Model", ensure Identity has sufficient permissions
Step 2: Verify Platform Target
Your application's platform target must match the application pool settings:
<!-- In .csproj -->
<PropertyGroup>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
Step 3: Check Native DLL Location
After publishing, verify the native DLLs are in the correct location:
/YourPublishedApp/
├── runtimes/
│ └── win-x64/
│ └── native/
│ ├── libSkiaSharp.dll
│ └── libHarfBuzzSharp.dll
├── YourApp.dll
└── web.config
If the runtimes folder is missing, your NuGet packages are not correctly configured.
Attempted Workarounds
Workaround 1: Add Explicit NativeAssets References
Approach: Explicitly reference the native asset packages for your target platform.
<PackageReference Include="SkiaSharp.NativeAssets.Win32" Version="2.88.7" />
<PackageReference Include="HarfBuzzSharp.NativeAssets.Win32" Version="7.3.0" />
For Linux:
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.7" />
Limitations:
- Package versions must match exactly
- Different packages needed for different platforms
- May still fail in some configurations
Workaround 2: Copy Native DLLs Manually
Approach: Post-build copy native DLLs to output directory.
<Target Name="CopyNativeDlls" AfterTargets="Build">
<Copy SourceFiles="$(NuGetPackageRoot)skiasharp.nativeassets.win32\2.88.7\runtimes\win-x64\native\*.dll"
DestinationFolder="$(OutDir)" />
</Target>
Limitations:
- Fragile and version-dependent
- Must update when package versions change
- Different paths for different platforms
Workaround 3: Configure IIS Application Pool
Approach: Ensure correct bitness and identity for IIS application pool.
# Set 64-bit mode
Set-ItemProperty IIS:\AppPools\YourAppPool -Name enable32BitAppOnWin64 -Value $false
Limitations:
- Only addresses IIS-specific issues
- May conflict with other application requirements
- Requires infrastructure changes
Workaround 4: AWS Lambda Custom Layer
Approach: Create a Lambda layer with the required native libraries.
# Create layer structure
mkdir -p layer/lib
cd layer
# Copy required libraries from a working Linux environment
cp /usr/lib/x86_64-linux-gnu/libfontconfig.so.1 lib/
cp /usr/lib/x86_64-linux-gnu/libfreetype.so.6 lib/
# Package the layer
zip -r questpdf-layer.zip lib/
# Upload to AWS Lambda as a layer
aws lambda publish-layer-version \
--layer-name questpdf-dependencies \
--zip-file fileb://questpdf-layer.zip \
--compatible-runtimes dotnet8
Limitations:
- Requires deep knowledge of Linux shared library dependencies
- Layer must be maintained separately from application
- Cold start times increase
- Lambda timeout limits may affect complex PDFs
Workaround 5: Google Cloud Run Configuration
Approach: Build a container with all dependencies for Cloud Run.
# Dockerfile for Google Cloud Run
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
# Install dependencies
RUN apt-get update && apt-get install -y \
libfontconfig1 \
libfreetype6 \
&& rm -rf /var/lib/apt/lists/*
COPY --from=build /app/publish .
# Cloud Run specific
ENV PORT=8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "YourApp.dll"]
Deploy with:
gcloud run deploy pdf-generator \
--image gcr.io/your-project/pdf-generator \
--platform managed \
--memory 2Gi \
--timeout 300
Limitations:
- Increased build complexity
- Container size increases
- Must track dependency updates
Workaround 6: Self-Hosted Linux Server Setup
Approach: Install required system libraries on the server.
For Debian/Ubuntu:
sudo apt-get update
sudo apt-get install -y \
libfontconfig1 \
libfreetype6 \
libpng16-16 \
libjpeg62-turbo
For CentOS/RHEL:
sudo yum install -y \
fontconfig \
freetype \
libpng \
libjpeg-turbo
Limitations:
- Requires server access and sudo permissions
- Must be repeated on each server
- May conflict with other applications
- Package names vary by distribution
A Different Approach: IronPDF
IronPDF bundles all native dependencies in the NuGet package, eliminating DLL-not-found errors.
Why IronPDF Avoids DLL Loading Issues
IronPDF's packaging strategy differs fundamentally:
- All-In-One Package: Native libraries are included in the main NuGet package
- Automatic Extraction: Libraries are extracted to the correct location at runtime
- Platform Detection: The correct binaries are loaded based on runtime detection
- No Manual Configuration: Works without additional package references or build steps
Code Example
using IronPdf;
public class ReportService
{
public byte[] GenerateReport(ReportData data)
{
// Works on IIS, Docker, Linux, Windows without DLL configuration
var renderer = new ChromePdfRenderer();
string html = BuildReportHtml(data);
using var pdf = renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
private string BuildReportHtml(ReportData data)
{
return $@"
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: Arial, sans-serif; margin: 40px; }}
table {{ width: 100%; border-collapse: collapse; }}
th, td {{ padding: 10px; border: 1px solid #ddd; }}
th {{ background-color: #4a90a4; color: white; }}
</style>
</head>
<body>
<h1>{data.Title}</h1>
<table>
<thead>
<tr>
{string.Join("", data.Headers.Select(h => $"<th>{h}</th>"))}
</tr>
</thead>
<tbody>
{GenerateRows(data.Rows)}
</tbody>
</table>
</body>
</html>";
}
private string GenerateRows(IEnumerable<string[]> rows)
{
return string.Join("", rows.Select(row =>
$"<tr>{string.Join("", row.Select(cell => $"<td>{cell}</td>"))}</tr>"));
}
}
Deployment (no special configuration):
<!-- Just reference IronPdf - no platform-specific packages needed -->
<PackageReference Include="IronPdf" Version="*" />
Key points:
- Single NuGet reference for all platforms
- No manual DLL copying required
- Works on IIS without application pool configuration
- Same code deploys to Docker, Linux, Windows
API Reference
For deployment documentation:
Migration Considerations
Licensing
- IronPDF is commercial software with perpetual licensing
- Free trial available for evaluation
- Licensing information
API Differences
- QuestPDF: Fluent C# API
- IronPDF: HTML/CSS-based rendering
- Fundamentally different document creation approaches
What You Gain
- Single package with all dependencies included
- Works across platforms without configuration
- No DLL loading errors in production
What to Consider
- Different document generation paradigm
- HTML templates instead of fluent API
- Commercial licensing required
Conclusion
QuestPDF's DllNotFoundException errors stem from the complexity of SkiaSharp's native dependencies across different platforms and deployment scenarios. The workarounds involve manual package references, build configuration, and infrastructure changes. For simpler deployments, libraries that bundle all dependencies in a single package eliminate this category of production issues.
Written by Jacob Mellor, CTO at Iron Software.
References
- QuestPDF GitHub Issue #1097{:rel="nofollow"} - QuestPdfSkia.dll not found on IIS
- QuestPDF GitHub Issue #812{:rel="nofollow"} - Unable to load shared library
For the latest IronPDF documentation and tutorials, visit ironpdf.com.
Top comments (0)