Skip to content

Native Deployment

Pragmatic.Imaging ships with a Rust-compiled native library (pragmatic_native.dll / .so / .dylib). This guide covers platform coverage, publish options, and Docker / container considerations.


The NuGet package carries native binaries for the RIDs below:

RIDFileStatus
win-x64pragmatic_native.dllShipped
linux-x64libpragmatic_native.soIn progress — build locally from native/ until the CI artifact lands
osx-arm64libpragmatic_native.dylibIn progress
linux-arm64libpragmatic_native.soPlanned
osx-x64libpragmatic_native.dylibPlanned
linux-musl-x64 (Alpine)Not planned (glibc required)

If your target isn’t win-x64, build the native library yourself from the repo’s Pragmatic.Imaging/native/ Rust project and copy the output into runtimes/{rid}/native/ of your publish output.

Track the platform-coverage issue on GitHub for progress.


For framework-dependent apps (the default dotnet publish), the native binary is picked up automatically from the NuGet’s runtimes/ folder. No extra configuration needed.

Terminal window
dotnet publish -c Release

The native binary ends up alongside your .dlls in publish/runtimes/{rid}/native/.


For self-contained apps (--self-contained true), MSBuild copies only the native binary for the target RID:

Terminal window
dotnet publish -c Release -r win-x64 --self-contained true

Result: the native binary is flattened into the publish output.


The binding is AOT-compatible — no reflection or dynamic code generation.

Terminal window
dotnet publish -c Release -r win-x64 -p:PublishAot=true

The native binary still lives alongside the AOT-compiled app. AOT affects the .NET code, not the Rust library.


The native library needs glibc on Linux. Recommended base images:

  • mcr.microsoft.com/dotnet/runtime:10.0-bookworm-slim — Debian, works
  • mcr.microsoft.com/dotnet/runtime:10.0-jammy — Ubuntu, works
  • mcr.microsoft.com/dotnet/runtime:10.0-alpinedoes NOT work (Alpine uses musl, not glibc)

For Alpine, either:

  1. Switch to a glibc-based image
  2. Build libpragmatic_native.so targeting musl yourself (non-trivial)
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish ./src/App -c Release -o /publish
FROM mcr.microsoft.com/dotnet/runtime:10.0-bookworm-slim
WORKDIR /app
COPY --from=build /publish .
ENTRYPOINT ["dotnet", "App.dll"]

The runtimes/linux-x64/native/libpragmatic_native.so is copied by dotnet publish automatically.


At startup, try calling a simple operation and catch the DllNotFoundException:

try
{
using var _ = ImagePipeline.Load(TestFixtures.TinyPng);
logger.LogInformation("Pragmatic.Imaging native library OK");
}
catch (DllNotFoundException ex)
{
logger.LogCritical(ex, "Pragmatic.Imaging native library not found — check deployment");
throw;
}

The library loads lazily on first use, so an app without image-processing work won’t notice a missing binary until the first request.


The Rust source lives at Pragmatic.Imaging/native/:

Terminal window
cd Pragmatic.Imaging/native
cargo build --release
# Output at target/release/libpragmatic_native.{so,dylib} or pragmatic_native.dll
# Copy into your app's runtime folder
cp target/release/libpragmatic_native.so \
../src/Pragmatic.Imaging/runtimes/linux-x64/native/

Requires Rust 1.80+. See Pragmatic.Imaging/native/README.md for build instructions and feature flags.


The native library and managed binding are versioned together. Mixing mismatched versions (old managed + new native, or vice versa) produces undefined behaviour — usually an immediate crash on the first FFI call.

The MSBuild targets in the NuGet ensure the binary shipped matches the managed DLL. If you build a native binary yourself, match the version tagged in Pragmatic.Imaging/native/Cargo.toml.


See troubleshooting.md for common deployment issues:

  • “Unable to load DLL ‘pragmatic_native’”
  • Alpine / musl failures
  • Self-contained publish missing the native file