1 Choose a plan

Consumption Plan is not supported. It aggressively recycles instances and has limited memory. Chromium cannot start reliably.

Use a Premium (EP1+) or Dedicated (B2+) plan. Both Windows and Linux are supported — choose whichever fits your environment.

  • Windows plan — No special Chromium configuration needed. CobaltPDF uses the bundled Windows binary automatically.
  • Linux plan — Requires ConfigureForAzure to set Chromium sandbox and GPU flags.

2 Create the project

Terminal
func init CobaltPdfFunc --dotnet-isolated -n net8.0
cd CobaltPdfFunc
dotnet add package CobaltPDF

3 Configure the engine

The configuration differs by OS. On Windows, no cloud preset is needed. On Linux, call ConfigureForAzure.

Linux plan

Program.cs
using CobaltPdf;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        services.AddCobaltPdf(o =>
        {
            CloudEnvironment.ConfigureForAzure(o);
            o.MaxSize = 2;
        });
    })
    .Build();

host.Run();

Windows plan

Program.cs
using CobaltPdf;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        // No cloud preset needed on Windows
        services.AddCobaltPdf(o =>
        {
            o.MaxSize = 2;
        });
    })
    .Build();

host.Run();

4 Create the HTTP function

This function accepts HTML in the POST body and returns a PDF file. The CobaltEngine is shared across all invocations via DI.

RenderPdf.cs
using System.Net;
using CobaltPdf;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;

namespace CobaltPdfFunc;

public class RenderPdf
{
    private readonly CobaltEngine _engine;

    public RenderPdf(CobaltEngine engine)
        => _engine = engine;

    [Function("RenderPdf")]
    public async Task<HttpResponseData> Run(
        [HttpTrigger(AuthorizationLevel.Function,
            "post")] HttpRequestData req)
    {
        var html = await new StreamReader(req.Body)
            .ReadToEndAsync();

        var pdf = await _engine
            .WithPaperFormat("A4")
            .PrintBackground()
            .RenderHtmlAsPdfAsync(html);

        var response = req.CreateResponse(
            HttpStatusCode.OK);
        response.Headers.Add(
            "Content-Type", "application/pdf");

        await response.Body.WriteAsync(pdf.BinaryData);
        return response;
    }
}

5 Deploy

Do not use -r linux-x64 or --self-contained. Both flatten the runtimes/ folder and break Chromium path resolution.

Azure Functions Core Tools

Terminal
dotnet publish -c Release -o ./publish
cd publish
func azure functionapp publish <YOUR_APP_NAME>

Visual Studio

Right-click the project → Publish → select your Function App. Visual Studio publishes without -r by default.

GitHub Actions

.github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: "8.0.x"
      - run: dotnet publish -c Release -o ./publish
      - uses: Azure/functions-action@v1
        with:
          app-name: <YOUR_APP_NAME>
          package: ./publish
          publish-profile: ${{ secrets.PUBLISH_PROFILE }}

6 Test

Terminal
curl -X POST \
  "https://<APP>.azurewebsites.net/api/RenderPdf?code=<KEY>" \
  -d "<h1>Hello from Azure!</h1>" \
  -o output.pdf

Troubleshooting

Chromium path not found

Your publish or CI/CD pipeline is using -r linux-x64. Remove it. Use dotnet publish -c Release -o ./publish without -r.

Timeout on first invocation

Cold start takes 1-3 seconds while Chromium launches. Use a Dedicated plan with Always On enabled for latency-sensitive workloads.

Out of memory

Set MaxSize = 1 on EP1 (3.5 GB) or MaxSize = 2 on EP2+ (7 GB). Monitor via Metrics > Memory working set in the Azure Portal.

Chromium crashes on Consumption

Switch to a Premium (EP1+) or Dedicated (B2+) plan under App Service plan > Scale up.