---
title: 'A PDF service that only runs on your machine'
canonical: 'https://www.dealmobile.co.uk/writing/a-pdf-service-that-only-runs-on-your-machine'
url: 'https://www.dealmobile.co.uk/writing/a-pdf-service-that-only-runs-on-your-machine'
type: Post
published: '2026-11-02'
summary: 'PrintMD’s PDF service turns Markdown into a PDF with one request, and it refuses to be hosted. Why that’s on purpose, and how it stays locked down even on your own computer.'
---

# A PDF service that only runs on your machine

Published 2026-11-02

![Terminal output headed “Asked to serve the network, it will not start”. Started with PRINTMD_HOST=0.0.0.0, the service says it is refusing to start because it only binds a loopback address such as 127.0.0.1. Started with PRINTMD_AUTH=token, it says this edition’s PDF API runs only on your own computer and hosting it is not supported.](https://www.dealmobile.co.uk/sites/default/files/styles/almanac_shot_md/public/content/images/posts/a-pdf-service-that-only-runs-on-your-machine/hero.png.webp?itok=egbjWDlP)

[PrintMD](https://www.dealmobile.co.uk/projects/printmd.md) has a small PDF service. You send it Markdown and it sends back a PDF. I use it from scripts and from other tools, because one command is easier than opening an editor.

You might expect me to say you can host it for your team. You can’t, and this post is about why that’s on purpose.

## What it does

The service drives a browser through Playwright and prints with it. The request is the Markdown and a few options: template, page size, layout, margin. The answer is the PDF, with headers that report the page count and any warnings about the document.

![A terminal running curl against the PDF service’s render address with the Dealmobile template and notes.md. The response headers shown are HTTP/1.1 200 OK, Content-Type application/pdf, X-PrintMD-Pages 4 and X-PrintMD-Warnings %5B%5D.](https://www.dealmobile.co.uk/sites/default/files/styles/almanac_shot/public/content/images/posts/a-pdf-service-that-only-runs-on-your-machine/render-headers.png.webp?itok=Z2l1z2rc&v=c041d6cc)

*A real render. %5B%5D is an empty list, URL-encoded: no warnings. It listens on port 8787 by default; this one ran on 8791.*

By default that’s the Chromium Playwright downloads; set `PRINTMD_CHROME` and it uses your own Chrome instead. Left to itself, the browser stays open between renders. On Linux, the user service that `server/service/install-service.sh` installs closes it a minute after the last render, so between renders the service needs about as much memory as a small Node process. I haven’t put an exact number on that.

## Why it won’t be hosted

A service that turns any text it’s sent into a rendered page, on a machine with a network, is a place where things go wrong. Someone sends a document that asks the browser to fetch an internal address, or to burn minutes of CPU, or to render a page the size of a billboard. Every one of those is something I’d have to defend against, forever, for a tool whose job is to make nice PDFs.

On your own machine most of those questions go away, because the only person sending documents is you. So the service has one mode: local. It listens on 127.0.0.1, or another loopback address, and won’t start if you point it at anything else, or ask it to run behind a token or a login. That’s a decision, not a missing feature.

## It’s still locked down

Local doesn’t mean careless. A Markdown file can contain images and links, and you may paste in something you didn’t write. So the render page is closed off:

- It can only load files that belong to the app. A request for anything else is blocked and listed in the response’s warnings header.
- The print page has a content security policy with scripts, frames and connections switched off.
- The Markdown can be up to 2 MB, not counting embedded images, a request up to 15 MB and one image up to 5 MB, and a render gets 20 seconds.
- It renders two documents at a time with a short queue, and answers “busy” to the rest.

## The editor

The editor is a different case. It’s a static page, and nothing in it sends your documents anywhere, so hosting it is fine. When it’s hosted it says so:

![The notice the hosted editor shows: This is a hosted copy. For the best results, run it on your own computer: the PDF service only works there, and it makes the best PDFs. A Dismiss button sits beside it.](https://www.dealmobile.co.uk/sites/default/files/styles/almanac_shot/public/content/images/posts/a-pdf-service-that-only-runs-on-your-machine/hosted-notice.png.webp?itok=t7ZC-FWx&v=393a22bf)

*The notice on the hosted editor, which can be dismissed.*

The editor makes its PDFs through the browser’s print dialog, hosted or not. The service is the other way to make one, and that only runs on your own computer.

## What this costs

If you want a shared PDF service for a team, this is the wrong tool, and I don’t want to pretend otherwise. If you want good PDFs from your own notes, it’s a few commands and a folder.

- **Projects:** [PrintMD](https://www.dealmobile.co.uk/projects/printmd.md)
- **Tags:** [Security](https://www.dealmobile.co.uk/writing/tag/security.md), [Markdown](https://www.dealmobile.co.uk/writing/tag/markdown.md), [PrintMD](https://www.dealmobile.co.uk/writing/tag/printmd.md)

---

Index of this site: [llms.txt](https://www.dealmobile.co.uk/llms.txt)
