|
|
|
Deployment to Docker (Linux and Windows Containers) |
|
Docker packages an application together with everything it needs to run into a portable container image, which then runs identically on a developer machine, an on-premise server or any cloud container service - Azure Container Apps and AKS, Amazon ECS and EKS, Google Cloud Run, and so on.
This library (Select.HtmlToPdf) is not supported in Docker containers. For container deployments - Linux or Windows - use SelectPdf.HtmlToPdf.Universal, the free cross-platform Community Edition. It keeps the classes and members of this library; porting existing code means new package references, the SelectPdf.Universal namespace instead of SelectPdf, and SelectPdf's own drawing types instead of System.Drawing. Like this library, the free edition generates PDF documents up to 5 pages long.
Linux containers (recommended) - SelectPdf.HtmlToPdf.Universal was designed for exactly this scenario and runs on the standard .NET runtime container images with no extra setup.
Windows containers - SelectPdf.HtmlToPdf.Universal is supported on Windows Server Core base images.
See Cross-Platform Library for the full feature list, the NuGet packages, platform support, and migration notes, and Migrating to SelectPdf.HtmlToPdf.Universal for every code change.
Reference the managed SelectPdf.HtmlToPdf.Universal package together with the SelectPdf.Universal.Native.linux-x64 (or SelectPdf.Universal.Native.linux-arm64) package. dotnet publish places the native engines in the output automatically, and nothing else needs to be installed in the image - SelectPdf.HtmlToPdf.Universal works on the standard Microsoft .NET runtime and ASP.NET container images (for example mcr.microsoft.com/dotnet/aspnet:8.0) out of the box:
No extra OS packages. The native Linux package bundles the complete Chromium shared-library closure - including the modules loaded at runtime for HTTPS - so no apt-get install step is required in the Dockerfile.
No display server. The engine renders headlessly - no X server, no Xvfb, no $DISPLAY configuration.
No font packages. Standard fallback fonts are bundled with the library and are used automatically when the container image has no fonts installed. System fonts, when present, still take priority.
No special docker run flags. No --no-sandbox, no added capabilities, no privileged mode - a plain docker run works.
No manual chmod. The library restores the engine's execute permission automatically before launching it - relevant when the deployment was produced on Windows, where the executable bit is lost.
No GPU and no --shm-size tuning. The engine renders in software and avoids /dev/shm, so Docker's small default shared-memory size is enough.
Base images. Any glibc-based .NET image works: the default Debian-based aspnet / runtime tags, the Ubuntu-based tags (8.0-jammy, 10.0-noble), and - through a self-contained publish - the RPM family such as amazonlinux:2023. The engines require glibc 2.30 or newer, which every mainstream distribution released since about 2020 provides (Ubuntu 20.04+, Debian 11+, RHEL 9+).
|
|
|---|
|
Use a glibc-based Linux image - the default Debian or Ubuntu based mcr.microsoft.com/dotnet tags. Alpine images are built on musl and are not supported by the native engines. |
Resources. Chromium-based rendering benefits from processor time and memory - plan for at least 1 core and 2 GB of RAM per container, and prefer 2 or more cores for consistently fast conversions under load.
A minimal Dockerfile is therefore just the standard .NET publish + runtime-image pattern - build and publish the application, copy the publish output into the runtime image, and run it. Complete working example (the project references SelectPdf.HtmlToPdf.Universal and SelectPdf.Universal.Native.linux-x64 - or .linux-arm64):
# build stage FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build WORKDIR /src COPY . . RUN dotnet publish MyApp.csproj -c Release -o /out # runtime stage - no apt-get, no font packages, no X server needed FROM mcr.microsoft.com/dotnet/aspnet:10.0 WORKDIR /app COPY --from=build /out ./ ENV ASPNETCORE_URLS=http://+:8080 EXPOSE 8080 ENTRYPOINT ["dotnet", "MyApp.dll"]
Build and run:
docker build -t myapp . docker run --rm -p 8080:8080 myapp
If restoring NuGet packages inside the Docker build is inconvenient - private feeds, offline build agents - publish on the host first (dotnet publish -c Release -o publish) and use a single-stage Dockerfile that only copies the publish output. The native engines are already in the publish folder, so the result is identical:
FROM mcr.microsoft.com/dotnet/aspnet:10.0 WORKDIR /app COPY publish/ . ENV ASPNETCORE_URLS=http://+:8080 EXPOSE 8080 ENTRYPOINT ["dotnet", "MyApp.dll"]
The default .NET images run as root, and everything above works unchanged. To run as the built-in non-root app user instead, grant world read and execute permission on the engine files before switching user:
RUN chmod -R a+rX /app/Chromium-CEF-* && chmod a+rx /app/SelectPdf.Tools USER app
For ARM64 containers, reference SelectPdf.Universal.Native.linux-arm64 instead of linux-x64 - everything else stays the same. The mcr.microsoft.com/dotnet base tags are multi-architecture, so the same image reference selects the matching architecture automatically. On an ARM64 host, build and run as usual; from an x64 host the image can be cross-built and run under emulation:
docker build --platform linux/arm64 -t myapp-arm64 . docker run --rm --platform linux/arm64 -p 8080:8080 myapp-arm64
|
|
|---|
|
This is also the package to use when building on an Apple Silicon Mac. Docker Desktop for Mac runs Linux containers, so a container image needs SelectPdf.Universal.Native.linux-arm64 - not osx-arm64, which is for applications running natively on macOS. |
SelectPdf.HtmlToPdf.Universal also supports Windows containers (reference SelectPdf.HtmlToPdf.Universal + SelectPdf.Universal.Native.win-x64) - the same application and image strategy can be reused across Windows and Linux containers. The following rules apply:
Use a Windows Server Core base image (for example mcr.microsoft.com/dotnet/aspnet:8.0-windowsservercore-ltsc2022). Server Core provides the graphics components the Chromium engine uses. Nano Server images are not supported - they do not ship those components.
Install the core Windows fonts in the image. Server Core images ship almost no fonts, and the Chromium engine cannot start on a font-less system - font fallback fails when none of the installed families are ones it recognizes, and a set of only third-party fonts is not sufficient. The image must have the standard Windows families installed (Segoe UI, Arial, Times New Roman, Verdana, Tahoma). Alternatively, base the image on the full mcr.microsoft.com/windows/server image, which ships the complete font set, and install the ASP.NET Core runtime in the Dockerfile - at the cost of a larger image.
No Visual C++ Redistributable is required - the native engines are static builds.
No engine configuration is needed. Starting with version 26.4, the Chromium-based rendering engine detects that it is running inside a Windows container and automatically switches to software rendering - no additional configuration or command-line switches are required.
Complete working example, including the font installation step (the project references SelectPdf.HtmlToPdf.Universal and SelectPdf.Universal.Native.win-x64):
# escape=`
# fonts stage - Server Core ships almost no fonts; copy the core families from the full Windows Server image
FROM mcr.microsoft.com/windows/server:ltsc2022 AS fonts
# build stage
FROM mcr.microsoft.com/dotnet/sdk:10.0-windowsservercore-ltsc2022 AS build
WORKDIR /src
COPY . .
RUN dotnet publish MyApp.csproj -c Release -o C:\out
# runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:10.0-windowsservercore-ltsc2022
COPY --from=fonts ["C:/Windows/Fonts/segoeui.ttf", "C:/Windows/Fonts/segoeuib.ttf", "C:/Windows/Fonts/segoeuii.ttf", "C:/Windows/Fonts/segoeuiz.ttf", "C:/Windows/Fonts/arial.ttf", "C:/Windows/Fonts/arialbd.ttf", "C:/Windows/Fonts/ariali.ttf", "C:/Windows/Fonts/arialbi.ttf", "C:/Windows/Fonts/times.ttf", "C:/Windows/Fonts/timesbd.ttf", "C:/Windows/Fonts/timesi.ttf", "C:/Windows/Fonts/timesbi.ttf", "C:/Windows/Fonts/cour.ttf", "C:/Windows/Fonts/courbd.ttf", "C:/Windows/Fonts/couri.ttf", "C:/Windows/Fonts/courbi.ttf", "C:/Windows/Fonts/verdana.ttf", "C:/Windows/Fonts/verdanab.ttf", "C:/Windows/Fonts/verdanai.ttf", "C:/Windows/Fonts/verdanaz.ttf", "C:/Windows/Fonts/tahoma.ttf", "C:/Windows/Fonts/tahomabd.ttf", "C:/Windows/Fonts/"]
RUN powershell -Command "Get-ChildItem C:\Windows\Fonts -Filter *.ttf | ForEach-Object { New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' -Name ($_.BaseName + ' (TrueType)') -Value $_.Name -PropertyType String -Force | Out-Null }"
WORKDIR /app
COPY --from=build C:\out ./
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApp.dll"]
Windows container images can only be built and run on a Windows host with Docker switched to Windows-container mode. When Windows itself is not a requirement, prefer the Linux containers above - they are smaller and need no font step at all.
For batch conversion jobs, package a console application instead of a web application and mount a host folder to collect the produced files:
docker run --rm -v "$PWD/out:/out" myconverter --url https://selectpdf.com -o /out/site.pdf
The container starts, performs the conversion, writes the PDF to the mounted folder and exits.
The SelectPdf.Universal samples include complete Docker samples - multi-stage Dockerfiles with compose.yaml files and per-sample instructions, containerizing a sample web application and a command-line converter, for both Linux and Windows containers. They are a working starting point for your own images.
The images built this way are standard OCI images - push them to any container registry (Azure Container Registry, Amazon ECR, Docker Hub, GitHub Container Registry) and deploy them from your orchestrator as usual. For the Azure services in particular, see Deployment to Microsoft Azure.