Build your PDF service
From zero to your own PDF microservice — the whole cycle.
Get PDF Server
Pull the base image — a Go service with a headless browser already inside.docker pull registry.gitlab.com/c0va23/pdf-server:latest-chromiumCreate a template folder
A template is just a folder — one per document. Everything it needs lives beside it.mkdir -p templates/invoiceWrite the template
Plain HTML with placeholders for your data. Mustache, Handlebars, Gohtml/template, or static HTML — chosen by file extension.cat > templates/invoice/template.mustache <<'HTML'<link rel="stylesheet" href="assets/style.css" /><h1>Invoice for {{customer}}</h1><p class="total">Total: {{total}}</p>HTMLDescribe the data with a schema
Add an optional JSON Schema. Incoming requests are validated against it before rendering — invalid data gets a clear422, never a broken PDF.cat > templates/invoice/schema.json <<'JSON'{"type": "object","required": ["customer", "total"],"properties": {"customer": { "type": "string" },"total": { "type": "number" }}}JSONAdd assets
Reference CSS, fonts, images, and SVG from your HTML — they load just like a normal web page.mkdir -p templates/invoice/assetscat > templates/invoice/assets/style.css <<'CSS'h1 { color: #5d2f86 }.total { font-weight: bold }CSSDebug with live preview
Run the base image with your templates mounted, then open the built-in tester UI: pick a template, edit the data, and watch the PDF re-render on every save.docker run -p 9999:9999 \-v $PWD/templates:/src/templates \-e TEMPLATES_RELOAD_MODE=always \registry.gitlab.com/c0va23/pdf-server:latest-chromiumBuild your container image
Packaging is a file copy on top of the base image — no app build, no code. Write theDockerfile, then build your image.cat > Dockerfile <<'DOCKERFILE'FROM registry.gitlab.com/c0va23/pdf-server:latest-chromiumCOPY templates /src/templatesRUN pdf-server validateDOCKERFILEdocker build -t your-org/pdf-service .validaterenders every example at build time — a broken template fails the build, not production.Deploy it
Run it like any container — Docker, Compose, Kubernetes — inside your own network. Your data never leaves it.docker run -p 9999:9999 your-org/pdf-servicePooled browsers, OpenTelemetry tracing, structured logs, and a status endpoint are built in.
Send a POST request, get a PDF
One API call from your application. That's the whole integration.curl -X POST localhost:9999/templates/invoice/render \-d '{"customer": "ACME", "total": 90}'The response body is the finished PDF (
application/pdf).
Get PDF
What happens inside your container on every request.
Validate the data
If the template ships aschema.json, the incoming JSON is checked against it first.Invalid data is rejected with a clear
422and the validation errors — a broken PDF is never produced.Render HTML from your template
Your template is filled with the data — body, header, and footer.<h1>Invoice for {{customer}}</h1>Mustache, Handlebars, Go
html/template, or static HTML — chosen by file extension, with partials.Open it in a real browser
A headless Chromium/Firefox borrowed from the pool loads the page together with your assets — CSS, fonts, images.Driven over the Chrome DevTools Protocol. SVG and JS charts work too: lifecycle events wait for async content.
Compose, if needed
A composition merges several sub-renders — cover, invoice, terms — into one document.Data-driven plans (YAML with jq expressions, or JavaScript) can mix local templates, raw HTML, and remote documents.
Print to PDF
The browser's print engine produces the pages — the same output as the browser's Print to PDF.{"pdf": {"landscape": true, "printBackground": true}}Your
params.jsoncontrols paper size, margins, orientation, headers and footers.The PDF comes back
Returned in the same HTTP response — no polling, no intermediate storage.Curious how it looks? See real rendered PDFs in the gallery →
Why PDF Server?
- One
docker build, no browser plumbing. Your image is a file copy on top of the base image — the headless browser and all its dependencies are already inside. Nothing to install, patch, or wire up. - Develop and debug with a live tester UI. Edit a template and watch the PDF re-render instantly — pick a template, tweak the data, see the result. Catch problems on your laptop, not in production.
- Runs on your own infrastructure. Deploy the container wherever you like, inside your own network, and scale it out with more replicas as load grows.
- Bring your favorite frontend tools. Templates are just HTML and CSS, so you can author them with the toolchain you already use — with live rebuild — and let PDF Server print the result.
See the Overview for the full picture or jump straight to the Quickstart.