MAATRIX / Блог / Gotenberg на VPS: создаём PDF-счета и отчёты из HTML

Gotenberg на VPS: создаём PDF-счета и отчёты из HTML

MAATRIX · 17 октября 2026 · 8 мин чтения

В браузере отчёт выглядит хорошо. После печати заголовок остаётся на первой странице, таблица уезжает на вторую, а последняя строка с итогом одиноко занимает третью. Так обычно начинается знакомство с автоматической генерацией PDF: файл уже есть, документ ещё предстоит получить.

Gotenberg предоставляет HTTP API для преобразования документов. Один из его маршрутов принимает HTML и связанные ресурсы, запускает Chromium и возвращает PDF. Это удобно для счетов, актов, подборок и регулярных отчётов, где данные меняются, а оформление должно оставаться предсказуемым.

Ниже соберём небольшую службу для доверенных шаблонов собственного приложения. Она будет доступна только внутри сервера. Публичный конвертер произвольных страниц требует отдельной защиты и в этот пример не входит.

VPS для автоматической генерации документов

Разместите Gotenberg рядом с приложением на VPS MAATRIX. Для небольших отчётов начните проверку с 2 vCPU и 4 GB памяти, ограничив параллельные задачи.

ВЫБРАТЬ VPS

Разделим расчёт и печать

Суммы, скидки и налоги должны рассчитываться в основной системе по её правилам. HTML-шаблон получает готовые значения и отвечает за отображение. Иначе однажды округление в JavaScript, базе и шаблоне даст три разных итога, а PDF окажется самым убедительным из них.

Храните версию шаблона вместе с данными сформированного документа. Если через месяц изменится оформление, должно быть понятно, какой вариант использовался при отправке. Для важных документов полезно сохранять готовый PDF, а не только надеяться заново получить идентичный результат.

Пример процесса: приложение создаёт запись отчёта, формирует HTML из проверенных данных, отправляет его в Gotenberg, проверяет ответ и сохраняет файл. Затем пользователю выдаётся ссылка с подходящими правами доступа. Ошибка конвертации оставляет задачу в состоянии ожидания или ошибки, но не помечает документ отправленным.

Почему сервису нужен запас памяти

Внутри работает браузерный движок. Несколько тяжёлых страниц с графиками и изображениями потребляют заметно больше ресурсов, чем обычный JSON API. Если все запросы запустить одновременно, небольшая машина может потратить всё время на борьбу за память.

Для пилота с умеренным количеством небольших документов подойдёт отправная точка MAATRIX Uab Start: 2 vCPU и 4 GB памяти. Для параллельных отчётов, графиков и крупных таблиц разумнее проверить Heka Standard с 4 vCPU и 8 GB. Это начальные варианты для испытания, а не обещание фиксированного числа документов в минуту.

Используйте поддерживаемую Ubuntu с Docker. Если основное приложение уже находится на другом VPS, соедините службы через закрытую сеть и настройте взаимный доступ. Порт конвертера не должен быть доступен всем посетителям интернета.

Диск нужен для образа, временных файлов и, если вы храните результат здесь, архива документов. Архив лучше считать отдельно: количество документов, средний размер, срок хранения и резервные копии. Возможность удалить временный файл не означает, что можно удалить отправленный клиенту отчёт.

Запускаем Gotenberg локально

Официальный проект публикует образ gotenberg/gotenberg. Для первого знакомства можно использовать актуальную ветку 8, затем зафиксировать проверенный точный выпуск или digest. Следите за исправлениями безопасности: конвертер работает со сложными форматами и браузерным движком.

docker run -d \
  --name gotenberg \
  --restart unless-stopped \
  -p 127.0.0.1:3000:3000 \
  gotenberg/gotenberg:8

Этот запуск привязывает порт к localhost VPS. Если приложение находится в другом контейнере, удобнее общая внутренняя Docker-сеть без публикации порта наружу. Внутри контейнера адрес 127.0.0.1 указывает на сам контейнер, поэтому механически копировать его в настройки соседней службы нельзя.

Проверьте доступность:

curl --fail http://127.0.0.1:3000/health
docker logs --tail 50 gotenberg

Примеры установки приведены в документации Gotenberg. Успешный ответ health подтверждает состояние службы, но качество документа всё равно проверяется отдельным запросом.

Первый HTML без внешних зависимостей

Создайте в рабочем каталоге файл index.html. Это имя важно для маршрута преобразования HTML. Минимальный учебный документ может выглядеть так:

<!doctype html>
<html lang="ru">
<meta charset="utf-8">
<title>Учебный отчёт</title>
<style>
  body { font: 14px sans-serif; color: #17202a; }
  h1 { font-size: 24px; }
  table { width: 100%; border-collapse: collapse; }
  td { border-bottom: 1px solid #ccc; padding: 8px; }
  tr { break-inside: avoid; }
</style>
<h1>Отчёт № TEST-104</h1>
<table><tr><td>Услуга</td><td>4 900 ₽</td></tr></table>
</html>

Отправьте файл с сервера, где доступен локальный порт:

curl --fail --show-error \
  --request POST http://127.0.0.1:3000/forms/chromium/convert/html \
  --form files=@index.html \
  --output report.pdf

Маршрут и правила передачи ресурсов описаны в руководстве HTML → PDF. Откройте полученный файл в отдельном просмотрщике, проверьте кириллицу, знак валюты и число страниц. Это небольшой проверочный пример, а не готовая бухгалтерская форма.

Шрифты и изображения лучше приносить с собой

Ссылка на картинку с внешнего сайта создаёт новую зависимость. Сервер изображения может отвечать медленно, требовать авторизацию или поменять файл. Для регулярной генерации передавайте нужные ресурсы вместе с HTML и ссылайтесь на них по поддерживаемому локальному пути.

Проверьте лицензии шрифтов и наличие нужных символов. Если шрифт не загрузился, браузер выберет замену, и таблица может неожиданно стать шире. Для русского текста испытайте фамилии, длинные названия, знак номера, разные валюты и неразрывные пробелы.

Не полагайтесь на случайную задержку «подождать две секунды», если документ рисует график JavaScript-кодом. Используйте предусмотренный маршрутами механизм ожидания готовности и ограниченный тайм-аут. Ещё надёжнее передавать уже подготовленный SVG или изображение, когда интерактивность для печати не нужна.

Если данные пользователя попадают в шаблон, экранируйте их. Название компании не должно превращаться в HTML, который выполняет произвольный скрипт или обращается к чужому адресу. Доверенный шаблон и произвольный пользовательский HTML — разные уровни риска.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

Макет проверяют на неудобных данных

Составьте небольшой набор документов: одна строка, сто строк, длинное название услуги, пустое дополнительное поле и большая сумма. Убедитесь, что итог не обрезается, шапка таблицы ведёт себя предсказуемо, а страницы читаются после печати.

CSS для экрана и печати не обязан совпадать. При необходимости используйте правила @media print, задавайте поля страницы и разрывы в соответствии с возможностями выбранного маршрута. Следите, чтобы нижний колонтитул не накладывался на последнюю строку таблицы.

Не уменьшайте весь текст до нечитаемого размера ради одной широкой колонки. Иногда правильное решение — перенос строки, отдельная страница приложения или альбомный формат. PDF должен быть удобен человеку, а не только помещаться в тестовую рамку.

После обновления Gotenberg повторяйте эти проверки. Изменение браузерного движка может влиять на верстку, даже если ваш HTML остался прежним. Сравнивайте не только байты файлов, но и вид страниц: служебные метаданные могут отличаться при одинаковом изображении.

Очередь, повторы и защита службы

Поставьте перед конвертером ограниченную очередь заданий. Основное приложение принимает запрос, сохраняет его ID и отдаёт задачу исполнителю. Число параллельных преобразований подбирают измерением памяти и времени на ваших шаблонах.

Для каждого задания ограничьте размер входных данных, время выполнения и число повторов. При ошибке сохраните техническую причину без полного содержимого чувствительного документа. Если ответ потерян после успешного сохранения PDF, повтор должен находить существующий результат по ID, а не отправлять клиенту второй счёт.

Не предоставляйте Gotenberg доступ к сети шире необходимого. Возможность преобразовать произвольный URL может превратиться в обращение к внутренним службам. Используйте сетевые ограничения и поддерживаемые настройки безопасности текущего выпуска; не публикуйте API напрямую ради удобства интеграции.

Для наблюдения полезны длительность задач, размер очереди, ошибки и потребление памяти. Главная страница приложения может работать, пока конвертер уже двадцать минут занят одним неудачным документом.

Свяжите файл с конкретным заданием

Присваивайте задаче генерации собственный идентификатор до обращения к конвертеру. Сохраните его в базе рядом с состоянием обработки и итоговым путём. Для диагностического сопоставления можно передать идентификатор в предусмотренном Gotenberg заголовке трассировки, не включая в него фамилию или номер договора.

Результат сначала сохраняйте во временный файл. После успешного ответа проверьте тип содержимого и возможность открыть PDF, затем атомарно переместите его в окончательное место. Иначе приложение может выдать ссылку на частично записанный документ, пока загрузка ещё идёт.

Не называйте файлы только текущей датой: два задания за одну секунду способны столкнуться. Уникальный ID помогает и при повторе, и при разборе обращения пользователя. Человек видит понятное название, а внутренняя система использует устойчивый технический ключ.

Если хранение результата выполняется в объектном хранилище, проверяйте успешную загрузку до изменения статуса задания. Конвертация могла завершиться, а сохранение — нет. Эти этапы должны иметь отдельные состояния, чтобы повторить только неудачную часть.

Для периодического отчёта дополнительно сохраняйте период данных и время их получения. Тогда файл за сентябрь не окажется случайно пересчитан по состоянию базы на середину октября при повторной отправке.

Когда можно подключать настоящие документы

Перед запуском пройдите весь путь: запись в системе, создание HTML, преобразование, сохранение, выдача разрешённому пользователю и восстановление из копии. Проверьте также отказ в доступе постороннему человеку.

Назначьте срок хранения временных файлов и готовых результатов. Если документ должен оставаться неизменным, сохраняйте его контрольную сумму и версию шаблона. Резервируйте готовые файлы вместе с записями основной системы, чтобы не получить каталог ссылок на исчезнувшие документы.

Gotenberg хорошо работает как небольшая специализированная служба. Он получает подготовленный материал и возвращает печатный результат. Чем яснее эта граница, тем проще сопровождение: бухгалтерия не занимается CSS, браузерный движок не рассчитывает оплату, а разработчик перестаёт чинить отчёт вручную перед каждой отправкой.

При проверке шрифтов сохраните контрольный PDF и откройте его на другом компьютере. Если документ выглядит правильно только на машине разработчика, результат ещё не принят. Особое внимание уделите символам рубля, длинным наименованиям и числам с пробелами: именно такие мелочи потом превращают аккуратную таблицу в загадку для бухгалтерии.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →