Repository navigation
Description
Context
Our application uses Gotenberg to generate PDF files from DOCX documents.
In some cases, the application needs to generate a large number of PDFs sequentially. Before each conversion, it performs a request to the /health endpoint to check that the Gotenberg service is still available.
Description
When generating PDFs sequentially, the /health endpoint intermittently fails after several conversions, even though the Gotenberg service is still available.
The issue usually occurs after around 10–15 PDF conversions.
The PDF conversions themselves are performed sequentially: I wait for a PDF conversion to complete before starting the next one.
Reproduction
I tested the following Gotenberg versions:
8.28: issue not reproducible
8.29 → 8.36: issue reproducible
A minimal reproduction script is provided below.
The reproduction script assumes that a Gotenberg service is available at http://pdf:3000. The DOCX file used for the conversion is attached here: input.docx.
<?php
require_once 'External/autoload.php';
use GuzzleHttp\Client;
$client = new Client();
$url = 'http://pdf:3000';
$file = __DIR__ . '/input.docx';
for ($i = 1; $i <= 100; $i++) {
echo "$i: health check... ";
try {
$client->get("$url/health", ['timeout' => 30]);
echo "OK\n";
$client->post("$url/forms/libreoffice/convert", [
'multipart' => [[
'name' => 'files',
'contents' => fopen($file, 'r'),
'filename' => 'file.docx',
]],
]);
echo " conversion OK\n";
} catch (\Throwable $e) {
echo "FAILED\n";
echo $e::class . ': ' . $e->getMessage() . "\n";
exit(1);
}
}
Expected behavior
The /health endpoint should consistently return a successful response while the Gotenberg service is available, including when PDF conversions are performed sequentially.
Actual behavior
After several PDF conversions, the /health request can fail even though the Gotenberg service is still available.
The issue is intermittent, but typically occurs after around 10–15 conversions.
Adding a retry around the /health request allows the process to continue successfully, but it also forces us to wait for a potentially unnecessary amount of time before retrying.
Adding a sleep between PDF conversions does not appear to improve the situation.
Analysis
The issue appears to have been introduced in 8.29, since the same reproduction does not fail with 8.28 but does fail with versions up to 8.36.
The fact that retrying the /health request immediately succeeds makes me suspect a regression affecting the health check behavior under repeated conversion requests.
Do you know what might have changed in 8.29 that could explain this behavior? Is this a known issue?
Any feedback or insight would be greatly appreciated. Thank you very much for your help and for taking the time to look into this.
Activity
gulien commentedon Sep 3, 2026
Confirmed regression, now fixed. Thanks for bisecting it to 8.28, that was the key detail.
libreoffice-restart-afterdefaults to 10, so Gotenberg restarts LibreOffice in the background after every 10th conversion. In 8.28 a restarting process reported healthy; a refactor in 8.29 flipped that to unhealthy, so any/healthprobe landing in the restart window (roughly 150ms to 250ms) returned 503. Your conversions were never failing: requests arriving during a restart are requeued and served, not rejected. The burst of failures comes from the health library caching the DOWN result for 1s.Fixed in
88ddaed: planned restarts report healthy, crash-recovery restarts still report unhealthy. An integration test now reproduces your loop.Workaround until the next release:
LIBREOFFICE_RESTART_AFTER=0.Please reopen if it persists after upgrading.
adq-talbot commentedon Sep 4, 2026
Thank you very much for the quick investigation and for fixing the regression!
fix(supervisor): report healthy during planned process restarts
feat(container): update image docker.io/gotenberg/gotenberg (8.36.0 ➔…
chore(deps): update gotenberg/gotenberg docker tag to v8.37.0
chore(deps): update gotenberg/gotenberg docker tag to v8.37.0
chore(deps): update gotenberg/gotenberg docker tag to v8.37.0 (#1889)
feat(container): update image gotenberg/gotenberg (8.36.0 → 8.37.0) (#…
Upstream merge (#1)