Gotenberg alternative

Carbone vs Gotenberg

A factual comparison of two self-hosted document conversion APIs: public benchmark, features, API equivalents in cURL, Node.js and Python, and a step-by-step migration guide.

Last updated

47.0xHTML to PDF throughput, 4 threads
7.8xDOCX to PDF throughput, 1 thread, Carbone ICE
100+output formats, not only PDF
1endpoint for every conversion

Start for free Run Carbone with Docker

Carbone or Gotenberg: which one should you choose?

Gotenberg and Carbone overlap on HTML and Office to PDF conversion, but they are built for different jobs. Pick the one that matches what you actually need to produce.

Choose Gotenberg if...

  • You only need HTML or Office to PDF and you already render the HTML yourself.
  • You need URL to PDF or page screenshots out of the box.
  • Your HTML needs JavaScript to render (client-side charts, single-page apps): Carbone disables JavaScript in Chrome for security, so it only converts HTML that is already rendered.
  • You need a toolbox for existing PDFs: merge, split, rotate, flatten, stamp, bookmarks, Factur-X.
  • You require an MIT license and your volumes are modest.

Choose Carbone if...

  • You need throughput: parallel Chromium, LibreOffice and Carbone ICE workers, 47.0x more HTML to PDF conversions per second in the benchmark.
  • You generate documents from data: a JSON payload and a DOCX, XLSX, PPTX, ODT, HTML or Markdown template, merged and converted in the same call.
  • You need formats beyond PDF: DOCX, XLSX, ODT, CSV, Markdown, PNG, WebP and 100+ more.
  • You want production features built in: converter hardening, OnlyOffice engine, template storage, a visual Studio, HA cluster, or a managed EU Cloud API.

Carbone vs Gotenberg benchmark

Carbone published an open-source benchmark of free document converters, with the scripts, sample files and Docker commands needed to reproduce it: carboneio/document-converter-benchmark. Here are the Carbone and Gotenberg results, in requests per second (higher is better).

ToolConversionThreadsAvg latencyRequests per second
CarboneHTML to PDF (Chromium)47.74 ms626.12
CarboneDOCX to PDF (Carbone ICE)416.96 ms280.52
CarboneHTML to PDF (Chromium)118.03 ms272.48
CarboneDOCX to PDF (LibreOffice)449.07 ms99.87
CarboneDOCX to PDF (Carbone ICE)150.76 ms96.48
CarboneDOCX to PDF (LibreOffice)1169.21 ms29.31
GotenbergHTML to PDF (Chromium)4374.74 ms13.32
GotenbergDOCX to PDF (LibreOffice)1400.58 ms12.44
GotenbergHTML to PDF (Chromium)1478.42 ms10.41
  • HTML to PDF: with 4 threads, Carbone answers in 7.74 ms on average against 374.74 ms for Gotenberg, which is 47.0x more requests per second. With a single thread the gap is 26.2x.
  • DOCX to PDF with LibreOffice on both sides: on one thread, Carbone serves 2.4x more requests per second (29.31 against 12.44). Gotenberg runs a single LibreOffice process per container (its documentation recommends adding containers to scale), while one Carbone container with 4 threads reaches 99.87 requests per second, 8.0x a Gotenberg container.
  • DOCX to PDF with Carbone ICE: the converter built into Carbone, with no office suite involved, serves 96.48 requests per second on one thread (7.8x Gotenberg) and 280.52 with 4 threads.
  • Carbone does slightly more work than a pure converter: it also scans every document for Carbone tags before converting it.

Feature comparison

What each tool supports out of the box, based on the Carbone 5 and Gotenberg 8 documentation.

FeatureCarboneGotenberg
Conversion
HTML to PDFYes ChromiumYes Chromium
URL to PDFNo send the HTML content insteadYes
Word, Excel, PowerPoint, ODT to PDFYes LibreOffice, OnlyOffice or Carbone ICEYes LibreOffice
Markdown to PDFYes send the .md file as isPartial Markdown wrapped in an HTML page
Output formatsYes 100+ (PDF, DOCX, XLSX, PPTX, ODT, CSV, HTML, MD, PNG, JPG, WebP...)Partial PDF, plus PNG, JPEG and WebP screenshots
PDF to DOCX, PDF to image, Office to OfficeYes PDF to DOCX with OnlyOffice, PDF to ODT, PNG or JPG with LibreOfficeNo
ScreenshotsPartial first page of an HTML, Office or PDF file as PNG, JPG or WebP, rendered by LibreOffice, not ChromiumYes HTML, URL and Markdown, rendered by Chromium
PDF features
PDF/A and PDF/UAYes PDF/A-1 to PDF/A-4, PDF/UAYes PDF/A-1b, 2b, 3b and PDF/UA
Password protection and permissionsYesYes
WatermarksYes up to 4 text watermarksYes text, image or PDF watermarks and stamps
Headers and footers with page numbersYesYes
Merge PDFsPartial append PDFs to a generated document (:appendFile, Enterprise) or merge a batchYes dedicated route
Embedded attachments, Factur-X and ZUGFeRDYes :attachFile (Enterprise)Yes
Split, rotate, flatten, optimize, metadata, bookmarksNoYes
Templating
Inject JSON data into documentsYes DOCX, XLSX, PPTX, ODT, HTML, Markdown, XML...No render the HTML yourself
Loops, conditions, formatters, translationsYesNo
Charts, barcodes, dynamic imagesYes EnterpriseNo
Template storage, versioning and visual StudioYesNo
AI assistants
MCP serverYes official, hosted at mcp.carbone.io for Claude, ChatGPT, Cursor, VS Code and more, or self-hosted next to CarboneNo community projects only
Skill to write templates with AIYes official Carbone Skill (Agent Skills standard) for Claude, ChatGPT, Cursor, Copilot, Gemini and moreNo
Plugin in AI marketplacesYes MCP server and Skill in one plugin, on the Claude, ChatGPT, Azure and Mistral marketplacesNo
Operations
Parallel conversionsYes N LibreOffice, Chromium or ICE workers per serverPartial parallel Chromium conversions (6 by default), one LibreOffice process per container
Timeouts, crash recovery, automatic restartsYesYes
Asynchronous webhooksYesYes
Fetch inputs from URLs or cloud storagePartial images and appended PDFs from URLsYes URLs, S3, MinIO, GCS
Metrics and health checkYes OpenMetrics (Prometheus)Yes OpenTelemetry
Stateless horizontal scalingYes plus a synchronized HA cluster (Enterprise)Yes
Security
Converter hardening (macros, scripts, external links)Yes ANSSI and BSI guidelinesPartial outbound URL filtering
API authenticationYes JWT tokensYes basic auth or OIDC
Deployment and licensing
Self-hosted Docker imageYes carbone/carbone-eeYes gotenberg/gotenberg
Managed Cloud APIYes EU-hosted, free planNo
LicenseCommunity Edition free, Enterprise paidMIT
Official SDKsYes Node.js, Python, PHP, Go, Java, Rust, JavaScriptPartial official PHP client, community clients
Commercial support and SLAYesNo

API equivalents: Gotenberg to Carbone

Both tools run as a local Docker container. The examples below target self-hosted instances: Gotenberg on port 3000 with multipart forms, Carbone on port 4000 with a JSON body where the file is encoded in base64.

HTML to PDF

Gotenberg expects a file named index.html. Carbone accepts any file name and uses its Chromium engine when converter is "C". Send a complete page with its <html> element: Carbone detects the file type from its content, and reads a fragment like <h1>Hello</h1> as XML.

Gotenberg cURL
curl --request POST http://localhost:3000/forms/chromium/convert/html \
  --form files=@index.html \
  --output result.pdf
Carbone cURL
TEMPLATE=$(base64 < index.html | tr -d '\n')
curl --request POST 'http://localhost:4000/render/template?download=true' \
  --header 'Content-Type: application/json' \
  --data '{ "template": "'"$TEMPLATE"'", "convertTo": "pdf", "converter": "C" }' \
  --output result.pdf
Gotenberg Node.js
import { readFile, writeFile } from 'node:fs/promises';

const form = new FormData();
form.append('files', new Blob([await readFile('index.html')]), 'index.html');

const res = await fetch('http://localhost:3000/forms/chromium/convert/html', {
  method: 'POST',
  body: form
});
await writeFile('result.pdf', Buffer.from(await res.arrayBuffer()));
Carbone Node.js
import { readFile, writeFile } from 'node:fs/promises';

const res = await fetch('http://localhost:4000/render/template?download=true', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    template: (await readFile('index.html')).toString('base64'),
    convertTo: 'pdf',
    converter: 'C'
  })
});
await writeFile('result.pdf', Buffer.from(await res.arrayBuffer()));
Gotenberg Python
import requests

with open("index.html", "rb") as f:
    res = requests.post(
        "http://localhost:3000/forms/chromium/convert/html",
        files={"files": ("index.html", f, "text/html")},
    )
res.raise_for_status()
open("result.pdf", "wb").write(res.content)
Carbone Python
import base64, requests

with open("index.html", "rb") as f:
    template = base64.b64encode(f.read()).decode()

res = requests.post(
    "http://localhost:4000/render/template?download=true",
    json={"template": template, "convertTo": "pdf", "converter": "C"},
)
res.raise_for_status()
open("result.pdf", "wb").write(res.content)

Word, Excel or PowerPoint to PDF

The same request works for DOCX, XLSX, PPTX, ODT, ODS, ODP and RTF. With Carbone, "converter": "L" uses LibreOffice; "O" switches to OnlyOffice and "I" to Carbone ICE, the fastest engine for DOCX and ODT.

Gotenberg cURL
curl --request POST http://localhost:3000/forms/libreoffice/convert \
  --form files=@document.docx \
  --output result.pdf
Carbone cURL
TEMPLATE=$(base64 < document.docx | tr -d '\n')
curl --request POST 'http://localhost:4000/render/template?download=true' \
  --header 'Content-Type: application/json' \
  --data '{ "template": "'"$TEMPLATE"'", "convertTo": "pdf", "converter": "L" }' \
  --output result.pdf
Gotenberg Node.js
import { readFile, writeFile } from 'node:fs/promises';

const form = new FormData();
form.append('files', new Blob([await readFile('document.docx')]), 'document.docx');

const res = await fetch('http://localhost:3000/forms/libreoffice/convert', {
  method: 'POST',
  body: form
});
await writeFile('result.pdf', Buffer.from(await res.arrayBuffer()));
Carbone Node.js
import { readFile, writeFile } from 'node:fs/promises';

const res = await fetch('http://localhost:4000/render/template?download=true', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    template: (await readFile('document.docx')).toString('base64'),
    convertTo: 'pdf',
    converter: 'L'
  })
});
await writeFile('result.pdf', Buffer.from(await res.arrayBuffer()));
Gotenberg Python
import requests

with open("document.docx", "rb") as f:
    res = requests.post(
        "http://localhost:3000/forms/libreoffice/convert",
        files={"files": ("document.docx", f)},
    )
res.raise_for_status()
open("result.pdf", "wb").write(res.content)
Carbone Python
import base64, requests

with open("document.docx", "rb") as f:
    template = base64.b64encode(f.read()).decode()

res = requests.post(
    "http://localhost:4000/render/template?download=true",
    json={"template": template, "convertTo": "pdf", "converter": "L"},
)
res.raise_for_status()
open("result.pdf", "wb").write(res.content)

Markdown to PDF

Gotenberg renders Markdown through an index.html wrapper that calls {{ toHTML "document.md" }}. Carbone converts the Markdown file directly, and the same call can output DOCX or HTML instead of PDF.

Gotenberg cURL
# index.html must contain {{ toHTML "document.md" }}
curl --request POST http://localhost:3000/forms/chromium/convert/markdown \
  --form files=@index.html \
  --form files=@document.md \
  --output result.pdf
Carbone cURL
TEMPLATE=$(base64 < document.md | tr -d '\n')
curl --request POST 'http://localhost:4000/render/template?download=true' \
  --header 'Content-Type: application/json' \
  --data '{ "template": "'"$TEMPLATE"'", "convertTo": "pdf", "converter": "L" }' \
  --output result.pdf
Gotenberg Node.js
import { readFile, writeFile } from 'node:fs/promises';

// index.html must contain {{ toHTML "document.md" }}
const form = new FormData();
form.append('files', new Blob([await readFile('index.html')]), 'index.html');
form.append('files', new Blob([await readFile('document.md')]), 'document.md');

const res = await fetch('http://localhost:3000/forms/chromium/convert/markdown', {
  method: 'POST',
  body: form
});
await writeFile('result.pdf', Buffer.from(await res.arrayBuffer()));
Carbone Node.js
import { readFile, writeFile } from 'node:fs/promises';

const res = await fetch('http://localhost:4000/render/template?download=true', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    template: (await readFile('document.md')).toString('base64'),
    convertTo: 'pdf',
    converter: 'L'
  })
});
await writeFile('result.pdf', Buffer.from(await res.arrayBuffer()));
Gotenberg Python
import requests

# index.html must contain {{ toHTML "document.md" }}
with open("index.html", "rb") as html, open("document.md", "rb") as md:
    res = requests.post(
        "http://localhost:3000/forms/chromium/convert/markdown",
        files=[("files", ("index.html", html)), ("files", ("document.md", md))],
    )
res.raise_for_status()
open("result.pdf", "wb").write(res.content)
Carbone Python
import base64, requests

with open("document.md", "rb") as f:
    template = base64.b64encode(f.read()).decode()

res = requests.post(
    "http://localhost:4000/render/template?download=true",
    json={"template": template, "convertTo": "pdf", "converter": "L"},
)
res.raise_for_status()
open("result.pdf", "wb").write(res.content)

Paper size, margins, headers and footers

Gotenberg reads page settings from form fields and separate header.html and footer.html files. Carbone reads them from custom elements placed inside the HTML itself, so the request body doesn't change.

Gotenberg
curl --request POST http://localhost:3000/forms/chromium/convert/html \
  --form files=@index.html \
  --form files=@header.html \
  --form files=@footer.html \
  --form paperWidth=8.27 \
  --form paperHeight=11.7 \
  --form marginTop=1 \
  --form marginBottom=1 \
  --form landscape=true \
  --form printBackground=true \
  --output result.pdf
Carbone
<!-- index.html, sent with the same request as above ("converter": "C") -->
<!DOCTYPE html>
<html>
<body>
  <carbone-pdf-options paper-size="A4" landscape="true"
                       margin-top="1" margin-bottom="1" />

  <carbone-pdf-header>
    <div style="font-size: 10px; width: 100%; text-align: center;">ACME Corp.</div>
  </carbone-pdf-header>

  <carbone-pdf-footer>
    <div style="font-size: 10px; width: 100%; text-align: center;">
      Page <span class="pageNumber"></span> of <span class="totalPages"></span>
    </div>
  </carbone-pdf-footer>

  <h1>Quarterly report</h1>
</body>
</html>

All the options are listed in the HTML template documentation.

Password, watermark and PDF/UA

Both tools apply PDF options in the conversion request. Gotenberg reads them from form fields, Carbone from convertTo.formatOptions.

Gotenberg
curl --request POST http://localhost:3000/forms/libreoffice/convert \
  --form files=@document.docx \
  --form pdfua=true \
  --form userPassword=s3cr3t \
  --form watermarkSource=text \
  --form watermarkExpression=CONFIDENTIAL \
  --output protected.pdf
Carbone
TEMPLATE=$(base64 < document.docx | tr -d '\n')
curl --request POST 'http://localhost:4000/render/template?download=true' \
  --header 'Content-Type: application/json' \
  --data '{
    "template": "'"$TEMPLATE"'",
    "converter": "L",
    "convertTo": {
      "formatName": "pdf",
      "formatOptions": {
        "PDFUACompliance": true,
        "EncryptFile": true,
        "DocumentOpenPassword": "s3cr3t",
        "Watermark": "CONFIDENTIAL"
      }
    }
  }' \
  --output protected.pdf

See all PDF options and watermark options.

Merge PDFs

Gotenberg has a dedicated merge route. Carbone merges PDFs while generating a document: the :appendFile formatter appends PDF files, given as URLs or base64 data URIs, at the end of the rendered template. This is an Enterprise feature, included in every Carbone Cloud plan.

Gotenberg
curl --request POST http://localhost:3000/forms/pdfengines/merge \
  --form files=@cover.pdf \
  --form files=@annex-1.pdf \
  --form files=@annex-2.pdf \
  --output merged.pdf
Carbone
# cover.html is a complete HTML page whose <body> contains:
#   <h1>{d.title}</h1>
#   {d.annexes[i].url:appendFile} {d.annexes[i+1].url}
TEMPLATE=$(base64 < cover.html | tr -d '\n')
curl --request POST 'http://localhost:4000/render/template?download=true' \
  --header 'Content-Type: application/json' \
  --data '{
    "template": "'"$TEMPLATE"'",
    "converter": "C",
    "convertTo": "pdf",
    "data": {
      "title": "Contract 2026-042",
      "annexes": [
        { "url": "https://example.com/annex-1.pdf" },
        { "url": "https://example.com/annex-2.pdf" }
      ]
    }
  }' \
  --output merged.pdf

To merge many documents generated from the same template, use batch generation with "batchOutput": "pdf". It is included in Carbone Cloud. When self-hosted, it requires the Enterprise Edition and CARBONE_NB_REPORT_MAX_PER_BATCH set to the maximum number of documents per batch.

Asynchronous conversion with a webhook

Both tools can answer immediately and call your server when the file is ready. Gotenberg replies 204 No Content, then posts the PDF to your webhook. Carbone posts a JSON object with a renderId, and you download the file with GET /render/{renderId}. Webhooks are available in every edition, including the free Community Edition. When self-hosted, Carbone refuses webhook URLs on a local network by default (see CARBONE_SECURITY_LEVEL).

Gotenberg
curl --request POST http://localhost:3000/forms/libreoffice/convert \
  --header 'Gotenberg-Webhook-Url: https://my-app.com/webhook' \
  --header 'Gotenberg-Webhook-Events-Url: https://my-app.com/webhook/events' \
  --form files=@document.docx
Carbone
TEMPLATE=$(base64 < document.docx | tr -d '\n')
curl --request POST 'http://localhost:4000/render/template' \
  --header 'Content-Type: application/json' \
  --header 'carbone-webhook-url: https://my-app.com/webhook' \
  --header 'carbone-webhook-header-authorization: my-secret' \
  --data '{ "template": "'"$TEMPLATE"'", "convertTo": "pdf" }'

URL to PDF

Gotenberg can open a URL in Chromium and print it. Carbone doesn't fetch web pages: download the HTML first, then convert it like any HTML file.

Gotenberg
curl --request POST http://localhost:3000/forms/chromium/convert/url \
  --form url=https://example.com/invoice/42 \
  --output result.pdf
Carbone
curl --silent https://example.com/invoice/42 --output page.html
TEMPLATE=$(base64 < page.html | tr -d '\n')
curl --request POST 'http://localhost:4000/render/template?download=true' \
  --header 'Content-Type: application/json' \
  --data '{ "template": "'"$TEMPLATE"'", "convertTo": "pdf", "converter": "C" }' \
  --output result.pdf

Beyond conversion: fill a template with JSON data

This is where Carbone and Gotenberg differ most. With Gotenberg, you render the HTML with your own template engine before converting it. Carbone does both in one call: add {d.field} tags to an HTML, DOCX, XLSX, PPTX or Markdown file, and send your JSON in data.

# invoice.html: <html><body><h1>Invoice {d.id}</h1> <p>Customer: {d.customer.name}</p></body></html>
TEMPLATE=$(base64 < invoice.html | tr -d '\n')
curl --request POST 'http://localhost:4000/render/template?download=true' \
  --header 'Content-Type: application/json' \
  --data '{
    "template": "'"$TEMPLATE"'",
    "converter": "C",
    "convertTo": "pdf",
    "data": { "id": "2026-042", "customer": { "name": "ACME Corp." } }
  }' \
  --output invoice.pdf

Loops, conditions, number and date formatters, translations, charts and barcodes are described in the template design documentation.

How to migrate from Gotenberg to Carbone

Moving from Gotenberg to Carbone is mostly a matter of changing the URL and the request body. Run both side by side, compare the PDFs, then switch traffic.

1. Start Carbone next to Gotenberg

The full image ships LibreOffice, OnlyOffice and Chromium. Without a license key, it runs the free Community Edition.

docker run --rm -p 4000:4000 -e CARBONE_FACTORIES=4 carbone/carbone-ee:full

2. Replace each Gotenberg route with a single endpoint

Every conversion goes through POST /render/template. The input file and the converter field replace the route.

Gotenberg routeCarbone equivalent
POST /forms/chromium/convert/htmlHTML file, "converter": "C"
POST /forms/chromium/convert/urlDownload the HTML first, then "converter": "C"
POST /forms/chromium/convert/markdownMarkdown file, "converter": "L", no HTML wrapper
POST /forms/chromium/screenshot/htmlHTML file, "convertTo": "png", "jpg" or "webp", without converter: LibreOffice renders the first page, not Chromium
POST /forms/libreoffice/convertOffice file, "converter": "L", "O" (OnlyOffice) or "I" (Carbone ICE)
POST /forms/pdfengines/convertPDF file, "hardRefresh": true, SelectPdfVersion in formatOptions
POST /forms/pdfengines/encryptEncryptFile and DocumentOpenPassword in formatOptions
POST /forms/pdfengines/watermarkWatermark or Watermarks in formatOptions (text only)
POST /forms/pdfengines/merge:appendFile in a template, or "batchOutput": "pdf"
POST /forms/pdfengines/embed, /forms/pdfengines/factur-x:attachFile in a template
POST /forms/pdfengines/split, rotate, flatten, optimize, bookmarksNo equivalent: keep Gotenberg or a PDF library for these steps
GET /healthGET /status

3. Send the file as base64 JSON instead of multipart

Read the file, encode it in base64 and put it in the template field, as in the API equivalents above. For pure conversion, don't send a data field: Carbone then converts the file as is.

4. Map your options

GotenbergCarboneWhere
paperWidth, paperHeight (US Letter by default)paper-width, paper-height (inches) or paper-size="A4"<carbone-pdf-options> in the HTML
marginTop, marginBottom, marginLeft, marginRightmargin-top, margin-bottom, margin-left, margin-right<carbone-pdf-options>
landscape, scale, preferCssPageSizelandscape, scale, prefer-css-page-size<carbone-pdf-options>
printBackgroundprint-background<carbone-pdf-options>
nativePageRangespage-ranges<carbone-pdf-options>
header.html, footer.html<carbone-pdf-header>, <carbone-pdf-footer> with pageNumber and totalPagesInside the HTML
pdfa=PDF/A-2b, pdfua=true"SelectPdfVersion": 2, "PDFUACompliance": trueconvertTo.formatOptions
userPassword"EncryptFile": true, "DocumentOpenPassword"convertTo.formatOptions
watermarkSource=text, watermarkExpression"Watermark", or "Watermarks" for size, color, opacity and angleconvertTo.formatOptions
Gotenberg-Webhook-Urlcarbone-webhook-urlRequest header
Gotenberg-Webhook-Extra-Http-Headerscarbone-webhook-header-X, one header per valueRequest header
Gotenberg-Output-Filename"reportName", sent with a data field ({} for a pure conversion)Request body
--chromium-max-concurrencyCARBONE_FACTORIES or --factoriesServer
--api-timeout (30 seconds by default)CARBONE_MAX_GENERATION_TIME (60 seconds by default)Server

5. Check which edition you need

Frequently asked questions

Is Carbone a drop-in replacement for Gotenberg?

For HTML, Office and Markdown to PDF, yes: Carbone runs as a Docker container like Gotenberg and uses Chromium and LibreOffice under the hood. The request format changes: one JSON endpoint (POST /render/template) with the file encoded in base64, instead of one multipart route per converter. Page options move into the HTML with <carbone-pdf-options>. URL to PDF, screenshots of URLs and the routes that edit existing PDFs (split, rotate, flatten, bookmarks) have no direct equivalent.


Is Carbone free like Gotenberg?

Gotenberg is free and MIT-licensed. The Carbone Community Edition is free to self-host and covers HTML, Office and Markdown conversion, PDF options, asynchronous webhooks and templating with text data. Enterprise features (appending PDFs, batch generation, charts, dynamic images, HA cluster) require a license when self-hosted, and every Carbone Cloud plan includes them, starting with a free plan.


Is Carbone faster than Gotenberg?

In the public benchmark published by Carbone (October 2026, Carbone 5.16.0 against Gotenberg 8.38.1, same files and hardware), Carbone served about 47 times more HTML to PDF conversions per second with 4 threads. For DOCX to PDF on one thread, Carbone served about 2.4 times more conversions per second when both tools use LibreOffice, and 7.8 times more with Carbone ICE, the converter built into Carbone. Gotenberg runs a single LibreOffice process per container, while Carbone runs several LibreOffice, Chromium or Carbone ICE workers in parallel. The scripts are open source, so you can reproduce the test on your own documents.


Can Carbone convert a URL to PDF?

No. Carbone converts the content you send, not web pages it fetches. Download the HTML of the page first, make its stylesheets and images absolute or inline, then convert it with the Chromium engine ("converter": "C"). If URL to PDF is your main need, Gotenberg handles it natively.


How do I merge PDFs with Carbone?

Use the :appendFile formatter in a template: each PDF given as a URL or a base64 data URI is appended to the generated document, for example a cover page followed by annexes. To combine many documents generated from the same template, use batch generation with "batchOutput": "pdf". Both are available in Carbone Cloud and in the self-hosted Enterprise Edition.


Can I generate PDF/A, PDF/UA, password-protected or watermarked PDFs?

Yes. Set SelectPdfVersion (PDF/A-1 to PDF/A-4), PDFUACompliance, EncryptFile with DocumentOpenPassword, and Watermark (or up to 4 styled Watermarks) in convertTo.formatOptions, in the same request as the conversion. PDF/A forbids encryption, so generate archival and password-protected copies separately. Image and PDF watermarks, which Gotenberg supports, are not available in Carbone.


What does Carbone do that Gotenberg doesn't?

Carbone is also a document generation engine: it fills DOCX, XLSX, PPTX, ODT, HTML and Markdown templates with JSON data (loops, conditions, formatters, translations, charts) and outputs PDF or 100+ other formats such as DOCX, XLSX, CSV or PNG. It also offers OnlyOffice and Carbone ICE engines, template storage, a visual Studio, official SDKs and a managed EU-hosted Cloud API.


Can I run Carbone and Gotenberg side by side during a migration?

Yes. Carbone listens on port 4000 by default and Gotenberg on port 3000, so both containers can run on the same host. Send the same documents to both, compare the PDFs, then switch your traffic route by route.


Still have a question? Chat with us.

Try Carbone on your own documents

Run the benchmark on your files, or convert your first document in a few minutes with the free Cloud plan or the Community Edition Docker image.

Start for free Deploy with Docker Read the conversion API

Related pages: Document Conversion API, HTML templates, benchmark source code.

Trusted by 800+ paid customers in 40+ countries

Finance
Healthcare
Insurance
Legal
Public
Retail
Energy
Software
Telecom
Dior
Thales
Younited
Danfoss
CMA CGM
Illumina
KparK
Scalingo
French Ministry of the Armed Forces
BMW
Telus
Bestseller
Flagship Biosciences
Insly
Ninox
Sodexo