oneimage

SkiaSharp on Linux and Docker

Fix “Unable to load shared library 'libSkiaSharp'” on Linux, Docker and Alpine: which NativeAssets package to add, fontconfig, and a no-native alternative.

SkiaSharp runs on Linux, but the SkiaSharp package doesn’t bring the Linux native library with it. On Linux, Docker or Alpine you have to add a native-assets package to your application project yourself. For containers that’s SkiaSharp.NativeAssets.Linux.NoDependencies, which needs nothing installed in the image. SkiaSharp.NativeAssets.Linux also works but needs fontconfig in the image. This page shows each error, the fix for each, and the Dockerfiles we tested with SkiaSharp 4.153.1 on .NET 10. It ends with OneImage, a pure C# image library with no native binary to deploy.

The errors

We published one small SkiaSharp program (below) three ways and ran it in Microsoft’s stock mcr.microsoft.com/dotnet/runtime:10.0 (Ubuntu 24.04) and runtime:10.0-alpine images. Every failure starts with TypeInitializationException: “The type initializer for 'SkiaSharp.SkiaApi' threw an exception”, and then a DllNotFoundException: “Unable to load shared library 'libSkiaSharp' or one of its dependencies.” The lines after that tell you which problem you have:

The runtime lists every path it tried, so read the first specific line. SkiaSharp’s own troubleshooting guide makes the same split. If libSkiaSharp.so itself wasn’t found, the native-assets package is missing. If a library it needs, like libfontconfig.so.1, wasn’t found, the binary is there but the image lacks one of its dependencies.

Why SkiaSharp doesn’t include Linux

The SkiaSharp 4.153.1 listing shows its dependencies for net10.0: SkiaSharp.NativeAssets.macOS and SkiaSharp.NativeAssets.Win32, and no Linux package. SkiaSharp’s package documentation says the Linux, WebAssembly, Nano Server and WinUI assets “must be added manually”. The README explains why: “Because there are multiple distros of Linux, and we cannot possibly support them all, we have a separate NuGet package”. The published app confirms it. With only SkiaSharp, its runtimes folder held osx, win-arm64, win-x64 and win-x86, and nothing for Linux.

Fix 1: add the NoDependencies native assets

For Docker, this is the package SkiaSharp’s documentation recommends. It describes it as having “No fontconfig, no third-party deps” and being “Designed for minimal containers”. It covers glibc and musl (Alpine) on x64, x86, arm, arm64, riscv64 and loongarch64.

TerminalNuGet
dotnet add package SkiaSharp.NativeAssets.Linux.NoDependencies --version 4.153.1

Two details matter:

This is the program we tested. It loads a 3000×3002 JPEG, resizes it to 800 px wide with the documented Resize(SKImageInfo, SKSamplingOptions) overload and saves a JPEG:

Program.csSkiaSharp 4.153.1 · .NET 10
using SkiaSharp;

using SKBitmap input = SKBitmap.Decode("photo.jpg");
int height = input.Height * 800 / input.Width;

using SKBitmap resized = input.Resize(new SKImageInfo(800, height), new SKSamplingOptions(SKCubicResampler.Mitchell));
using SKData jpeg = resized.Encode(SKEncodedImageFormat.Jpeg, 85);
File.WriteAllBytes("photo-800.jpg", jpeg.ToArray());

Console.WriteLine($"{resized.Width}x{resized.Height}");

With NoDependencies it ran unchanged in the plain Ubuntu image, the Alpine image and the distroless runtime:10.0-noble-chiseled image. A Dockerfile needs nothing SkiaSharp-specific:

DockerfileSkiaSharp + NoDependencies
FROM mcr.microsoft.com/dotnet/runtime:10.0
WORKDIR /app
COPY publish/ .
ENTRYPOINT ["dotnet", "SkiaResize.dll"]

For the chiseled image, use FROM mcr.microsoft.com/dotnet/runtime:10.0-noble-chiseled and COPY --chown=app:app publish/ .. Chiseled images run as a non-root user, so the app needs to own any folder it writes to.

The tradeoff is fonts. Without fontconfig, SkiaSharp can’t enumerate installed fonts. Its documentation says that with NoDependencies “Fonts must be loaded explicitly”. If you only decode, resize and encode images, that doesn’t matter. If you draw text, load the font files you ship.

Fix 2: add NativeAssets.Linux and install fontconfig

If your app needs the system’s fonts, use SkiaSharp.NativeAssets.Linux and install fontconfig in the image. SkiaSharp’s selection guide recommends this package for a “Standard Linux server/desktop” and whenever the “App needs system font enumeration”. On Debian or Ubuntu images:

DockerfileUbuntu / Debian
FROM mcr.microsoft.com/dotnet/runtime:10.0
RUN apt-get update \
    && apt-get install -y --no-install-recommends libfontconfig1 \
    && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY publish/ .
ENTRYPOINT ["dotnet", "SkiaResize.dll"]

On Alpine:

DockerfileAlpine
FROM mcr.microsoft.com/dotnet/runtime:10.0-alpine
RUN apk add --no-cache fontconfig
WORKDIR /app
COPY publish/ .
ENTRYPOINT ["dotnet", "SkiaResize.dll"]

Both printed 800x800. Running ldd on the libSkiaSharp.so from this package in the stock image shows the missing piece: libfontconfig.so.1 => not found. To see fonts in the output, the image also needs font files. The stock Ubuntu and Alpine .NET 10 runtime images we used have no /usr/share/fonts directory.

Still failing? Check these

Image processing without a native library

The fixes above work, but the native binary has to match every platform and image you deploy to, and the native-assets package is one more reference to keep in step with SkiaSharp. If your SkiaSharp code does image processing (decode, resize, crop, convert, encode) rather than 2D drawing, GPU rendering or UI, OneImage removes the native part entirely. It is pure C#, licensed MIT OR Apache-2.0, and targets .NET 10.

TerminalNuGet
dotnet add package RecursiveAI.OneImage
Program.csOneImage 1.6.0 · .NET 10
using OneImage;

Image resized = Image.Load("photo.jpg").Resize(width: 800);
resized.Save("photo-800.jpg", new SaveOptions
{
    EncoderOptions = new JpegEncoderOptions { Quality = 85 }
});

Console.WriteLine($"{resized.Width}x{resized.Height}");

It printed 800x801, since OneImage rounds the derived height where the SkiaSharp sample truncates it. The same publish folder ran in the Ubuntu, Alpine and chiseled .NET 10 runtime images with nothing added. There was no runtimes/ folder and no .so file, and the whole publish output was 7.8 MB. No runtime identifier was needed, and this is the chiseled Dockerfile we used:

DockerfileOneImage
FROM mcr.microsoft.com/dotnet/runtime:10.0-noble-chiseled
WORKDIR /app
COPY --chown=app:app publish/ .
ENTRYPOINT ["dotnet", "OneImageResize.dll"]

OneImage also applies EXIF orientation when it loads, decodes JPEGs at reduced scale when a thumbnail allows it, and checks size limits from the file headers before allocating, which matters for uploads. See our guide to resizing images in C#. For drawing-heavy code, SkiaSharp stays the better tool. Our comparison of .NET image libraries says where each one fits, and the SkiaSharp license page covers what you agree to when you ship it.