Introduction
.NET applications often depend on native libraries (libssl, libicu, libc, etc.). When deployed in Docker containers, especially minimal images like Alpine Linux or distroless, these native dependencies may be missing. The error manifests as DllNotFoundException or Unable to load shared library at runtime, even though the application compiled and published successfully.
Symptoms
System.DllNotFoundException: Unable to load shared library 'libssl.so'System.IO.FileNotFoundException: libicuuc.so.72The type initializer for 'System.Net.Http.CurlHandler' threw an exception- Works on host machine but fails in Docker container
- Works on Debian-based image but fails on Alpine
Example error:
``
System.TypeInitializationException: The type initializer for
'Crypto' threw an exception.
---> System.DllNotFoundException: Unable to load shared library
'System.Security.Cryptography.Native.OpenSsl' or one of its dependencies.
libssl.so.3: cannot open shared object file: No such file or directory
Common Causes
- Alpine Linux uses musl libc instead of glibc
- Missing ICU libraries for globalization
- Missing OpenSSL libraries for cryptographic operations
- Publishing for wrong Runtime IDentifier (RID)
- Using
mcr.microsoft.com/dotnet/aspnetruntime image without required native libs
Step-by-Step Fix
- 1.Use the correct base image:
- 2.```dockerfile
- 3.# WRONG: Alpine does not have glibc, many native libs fail
- 4.FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine
# CORRECT: Use Debian-based image for native library compatibility FROM mcr.microsoft.com/dotnet/aspnet:8.0-bookworm-slim ```
- 1.Install missing native dependencies:
- 2.```dockerfile
- 3.FROM mcr.microsoft.com/dotnet/aspnet:8.0-bookworm-slim
# Install required native libraries RUN apt-get update && apt-get install -y --no-install-recommends \ libssl3 \ libicu72 \ libc6 \ zlib1g \ && rm -rf /var/lib/apt/lists/*
WORKDIR /app COPY . . ENTRYPOINT ["dotnet", "MyApp.dll"] ```
- 1.Use self-contained deployment with correct RID:
- 2.```bash
- 3.# Publish for specific Linux distribution
- 4.dotnet publish -c Release -r linux-x64 --self-contained true
- 5.# Or for ARM64
- 6.dotnet publish -c Release -r linux-arm64 --self-contained true
- 7.
`
FROM mcr.microsoft.com/dotnet/aspnet:8.0-bookworm-slim AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["./MyApp"] # Self-contained executable, not dotnet MyApp.dll- 1.Debug missing library dependencies:
- 2.```dockerfile
- 3.# Add debugging tools temporarily
- 4.FROM mcr.microsoft.com/dotnet/aspnet:8.0-bookworm-slim
- 5.RUN apt-get update && apt-get install -y ldd strace
# Find which native libraries the app needs COPY --from=publish /app/publish /app WORKDIR /app
# List missing dependencies RUN ldd MyApp.dll 2>&1 | grep "not found" # Or RUN find . -name "*.so" -exec ldd {} \; 2>&1 | grep "not found" ```
- 1.Disable globalization if not needed (reduces image size):
- 2.```dockerfile
- 3.ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true
- 4.ENV DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES=false
- 5.
`
// In Program.cs, disable invariant mode if using globalization:
// This is set via environment variable, not code
// DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1Prevention
- Use Debian-based images (not Alpine) unless you have a specific need
- Run
lddon native dependencies during the build to catch missing libraries - Use
dotnet publish -rwith the correct target RID - Add health check endpoints that verify native library availability
- Pin base image tags to specific versions (not
latest) - Use multi-stage builds to keep the final image small but complete