§HOW-TO GUIDE · C# · .NET 8 & 10

HTML to PDF on Azure App Service (Linux) in C#

An ASP.NET Core app with SelectPdf.Universal runs on App Service for Linux as it is: nothing is installed on the server and no setting is required, on the Basic plan or above.

Compiled and run: Azure App Service Linux .NET 10 image, x64 · Linux ARM64 · Linux x64 · macOS ARM64 · Windows x64

Video walkthrough · 3:33 · Watch on YouTube · Short · 0:58
§STEP BY STEP

Build it step by step.

  1. 1Create the project

    A web project with three packages. The engine package matches the machine that builds the app, so dotnet run works on Windows, and dotnet publish -r linux-x64 packs the Linux engine for App Service. The fonts package covers Chinese, Japanese, Korean and other scripts that the Linux image has no fonts for.

    PdfApp.csproj
    <Project Sdk="Microsoft.NET.Sdk.Web">
    
      <PropertyGroup>
        <TargetFramework>net10.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
      </PropertyGroup>
    
      <!-- the engine for the machine that builds the app; publishing with -r linux-x64 picks Linux,
           which is what App Service on Linux needs. Fonts: the Linux image has none for CJK and other scripts. -->
      <PropertyGroup>
        <_SelectPdfArch>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLowerInvariant())</_SelectPdfArch>
        <SelectPdfRid Condition="$([MSBuild]::IsOSPlatform('Windows'))">win-$(_SelectPdfArch)</SelectPdfRid>
        <SelectPdfRid Condition="$([MSBuild]::IsOSPlatform('Linux'))">linux-$(_SelectPdfArch)</SelectPdfRid>
        <SelectPdfRid Condition="$([MSBuild]::IsOSPlatform('OSX'))">osx-$(_SelectPdfArch)</SelectPdfRid>
        <SelectPdfRid Condition="'$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier)</SelectPdfRid>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="SelectPdf.Universal" Version="26.5.0" />
        <PackageReference Include="SelectPdf.Universal.Native.$(SelectPdfRid)" Version="26.5.0" />
        <PackageReference Include="SelectPdf.Universal.Fonts" Version="26.5.0" />
      </ItemGroup>
    
    </Project>
  2. 2A minimal web app

    Program.cs · lines 1–7
    using SelectPdf.Universal;
    
    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    // GET /report.pdf: a PDF made on the server, with text in Latin, Greek and CJK scripts
    app.MapGet("/report.pdf", async (CancellationToken cancel) =>

    One endpoint, GET /report.pdf. The CancellationToken is the request's.

  3. 3HTML in three scripts

    Program.cs · lines 9–13
        string html = """
            <h1>Quarterly report</h1>
            <p>English · Deutsch · Ελληνικά</p>
            <p>日本語 · 中文 · 한국어</p>
            """;

    Latin, Greek and CJK text. The CJK line needs SelectPdf.Universal.Fonts on Linux; without it, those characters would print as empty boxes.

  4. 4Convert and return the PDF

    Program.cs · lines 15–23
        HtmlToPdf converter = new HtmlToPdf();
        PdfDocument doc = await converter.ConvertHtmlStringAsync(html, cancel);
        byte[] pdf = doc.Save();
        doc.Close();
    
        return Results.File(pdf, "application/pdf");
    });
    
    app.Run();

    ConvertHtmlStringAsync renders the HTML and Save() returns the bytes. Without a file name, Results.File lets the browser show the PDF.

  5. 5Run it

    terminal
    dotnet run
    
    # then open the address dotnet run prints, followed by /report.pdf

    The request returns report.pdf. Without a license key, every page carries a trial watermark; set GlobalProperties.LicenseKey before the first conversion to remove it.

§COMPLETE PROGRAM

The complete Program.cs.

This is the exact file our samples pipeline compiled and ran on Azure App Service Linux .NET 10 image, x64 and Linux ARM64 and Linux x64 and macOS ARM64 and Windows x64 to produce the output on this page.

Program.cs
using SelectPdf.Universal;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// GET /report.pdf: a PDF made on the server, with text in Latin, Greek and CJK scripts
app.MapGet("/report.pdf", async (CancellationToken cancel) =>
{
    string html = """
        <h1>Quarterly report</h1>
        <p>English · Deutsch · Ελληνικά</p>
        <p>日本語 · 中文 · 한국어</p>
        """;

    HtmlToPdf converter = new HtmlToPdf();
    PdfDocument doc = await converter.ConvertHtmlStringAsync(html, cancel);
    byte[] pdf = doc.Save();
    doc.Close();

    return Results.File(pdf, "application/pdf");
});

app.Run();

Open the project on GitHub → this program and the variations below, ready to dotnet run

§DEPLOY

Deploy it.

Create a Linux plan and a .NET 10 web app, turn on Always On, and deploy the published app as a zip. Replace <app-name> with a name of your own; on Windows, create the zip with Compress-Archive publish\* app.zip.

terminal
az login
az group create --name selectpdf-rg --location westeurope
az appservice plan create --resource-group selectpdf-rg --name selectpdf-plan --sku P1v3 --is-linux
az webapp create --resource-group selectpdf-rg --plan selectpdf-plan --name <app-name> --runtime "DOTNETCORE:10.0"
az webapp config set --resource-group selectpdf-rg --name <app-name> --always-on true

dotnet publish -c Release -r linux-x64 --self-contained false -o publish
cd publish && zip -r ../app.zip . && cd ..
az webapp deploy --resource-group selectpdf-rg --name <app-name> --src-path app.zip --type zip

# then open https://<app-name>.azurewebsites.net/report.pdf

These steps, with the Windows variants, are in the documentation: selectpdf.com/pdf-library/html/Azure-App-Service.htm.

§OUTPUT

The PDF it produces.

First page of report.pdf, the PDF this sample produces
report.pdf, page 1

Download report.pdf ↓ the file this sample produced, unedited

§TROUBLESHOOTING

Common problems and fixes.

Characters print as boxes

Reference SelectPdf.Universal.Fonts: the App Service Linux image has no fonts for CJK and many other scripts, and a code deployment cannot install any.

Conversions are slow

Conversion is processor bound: Premium v3 converts several times faster than Basic or Standard. Turn on Always On so a request never meets a starting app.

The app was published on Windows

That works: the native package carries every library the engine needs, and SelectPdf restores the engine's execute permission, which a zip deployment loses. Publish with -r linux-x64.

§GOOD TO KNOW

Good to know.

  • App Service runs SelectPdf on Windows and Linux on the Basic plan or above, with the complete feature set; Free and Shared plans are not supported. Source
  • The Linux image has no fonts for Chinese, Japanese and Korean, and a code deployment cannot install any, so reference SelectPdf.Universal.Fonts. Source
  • On Premium v3 a warm Linux instance converts selectpdf.com in about 1.3 seconds; Basic and Standard take about four times longer. Source
  • Always On keeps the application loaded while idle, so the engine stays warm. Source
?FAQ

Questions, answered.

Short answers about this guide. The SelectPdf.Universal documentation covers every class and option.

SelectPdf.Universal runs on App Service for Linux on the Basic plan or above. Reference SelectPdf.Universal, SelectPdf.Universal.Native.linux-x64 and SelectPdf.Universal.Fonts; nothing is installed on the server.
No. A code deployment of the .NET 10 runtime stack works; the native package carries the engine and every library it needs.
Premium v3 for fast conversions: a warm P1v3 instance converts selectpdf.com in about 1.3 seconds on Linux. Basic or Standard plans work too, about four times slower. Free and Shared plans are not supported.
Yes, with SelectPdf.Universal.Native.win-x64 and a 64-bit worker process. Do not use Run From Package on Windows.