# How to self-host Gotenberg with Docker

Self-host Gotenberg with Docker in five minutes, lock it down for production, and learn when a hosted HTML-to-PDF API is the better deal.

[Gotenberg](https://gotenberg.dev) is an open-source (MIT) Docker image that wraps headless Chromium and LibreOffice behind a simple HTTP API. html-pdf.com runs it under the hood, so everything below comes from running it in production.

**Quick answer.** Start the container with `docker run --rm -p 3000:3000 gotenberg/gotenberg:8`, then post an `index.html` file to `/forms/chromium/convert/html`. If you would rather not run a server, the hosted API takes JSON and returns a PDF:

```bash
curl https://html-pdf.com/v1/pdf -H "Authorization: Bearer $HTML_PDF_KEY" \
  -d '{"html": "<h1>Hello</h1>"}' -o out.pdf
```

## 1. Start Gotenberg

This step gets a local Gotenberg server running with one command.

```bash
docker run --rm -p 3000:3000 gotenberg/gotenberg:8
```

Check that it is up.

```bash
curl http://localhost:3000/health
```

## 2. Convert HTML to PDF

Here you turn an HTML file or a live URL into a PDF. Gotenberg expects a `multipart/form-data` request with a file named exactly `index.html`.

```bash
echo '<h1>Hello from Gotenberg</h1>' > index.html
curl -F files=@index.html http://localhost:3000/forms/chromium/convert/html -o out.pdf
```

Useful form fields include `paperWidth` / `paperHeight` (inches or units like `210mm`), `marginTop`…`marginLeft`, `landscape=true`, `printBackground=true` and `waitDelay=2s`. You can send images, fonts and CSS as extra `files` and reference them by name from `index.html`.

To render a live web page instead, use the URL route.

```bash
curl -F url=https://example.com http://localhost:3000/forms/chromium/convert/url -o page.pdf
```

## 3. Lock it down before exposing it

These settings keep a public Gotenberg from becoming a security hole. By default it renders anything for anyone who can reach it, so fix that before it touches the internet.

- **Don't publish the port.** Keep it on a private Docker network and call it only from your app.
- **Add basic auth** if it must be reachable remotely.
  ```bash
  docker run -p 3000:3000 \
    -e GOTENBERG_API_BASIC_AUTH_USERNAME=me -e GOTENBERG_API_BASIC_AUTH_PASSWORD=long-random-secret \
    gotenberg/gotenberg:8 gotenberg --api-enable-basic-auth
  ```
- **Block SSRF.** HTML from users can point `<img>` or `<iframe>` at `http://169.254.169.254` or your internal services. Add `--chromium-deny-private-ips` (and `--api-disable-download-from` if you don't use that feature).
- **Turn off what you don't use.** Use `--libreoffice-disable-routes`, `--pdfengines-disable-routes` and `--webhook-disable`.
- **Bound resources.** Set `--api-timeout=30s`, `--chromium-max-queue-size=20`, and a memory limit on the container, because Chromium memory use spikes.

## 4. docker compose for production

This compose file puts the hardening flags together with your app on a private network.

```yaml
services:
  gotenberg:
    image: gotenberg/gotenberg:8
    restart: unless-stopped
    command:
      - gotenberg
      - --api-timeout=30s
      - --chromium-deny-private-ips
      - --api-disable-download-from
      - --libreoffice-disable-routes
      - --chromium-idle-shutdown-timeout=15m
    mem_limit: 2g
  app:
    build: .
    environment:
      GOTENBERG_URL: http://gotenberg:3000
    depends_on: [gotenberg]
```

`--chromium-idle-shutdown-timeout` lets Chromium sleep when idle, which saves memory on small servers. The first request after a pause takes about a second longer.

## 5. What you sign up for

This is the ongoing work that comes with self-hosting. The licence is free, but your time is not.

- **Updates.** Chromium ships security fixes constantly and Gotenberg releases follow. Pin a major version (`:8`) and pull regularly.
- **Memory.** Plan for 1-2 GB of RAM for comfortable concurrency.
- **Scaling and queues.** Bursts of large documents need horizontal scaling or a queue in front.
- **Monitoring.** You need health checks, restarts and logs.

## Or skip the server

If you only need HTML (or a URL) in and a PDF out, [html-pdf.com](https://html-pdf.com) gives you a hosted, hardened Gotenberg behind an API key.

```bash
curl https://html-pdf.com/v1/pdf -H "Authorization: Bearer $HTML_PDF_KEY" \
  -d '{"html": "<h1>Hello</h1>", "format": "A4"}' -o out.pdf
```

- JSON in, PDF out. No multipart and no `index.html` naming rules.
- SSRF protection, timeouts and updates are handled for you.
- A remote MCP server lets Claude, ChatGPT and Cursor make PDFs directly.
- 100 free pages, then pay per page (from $0.005). Credits never expire.

[Get a free API key](https://html-pdf.com/login) · [API reference](https://html-pdf.com/docs) · [Deploy Gotenberg on Railway](https://html-pdf.com/deploy-gotenberg-railway)

## FAQ

### Is Gotenberg free to use?

Yes. Gotenberg is open source under the MIT licence, so you only pay for the server you run it on and the time to maintain it.

### How much memory does Gotenberg need?

Plan for 1-2 GB of RAM for comfortable concurrency, and set a container memory limit because Chromium memory use spikes on large documents.

### How do I secure a self-hosted Gotenberg?

Keep the port on a private Docker network, add basic auth if it must be remote, and use `--chromium-deny-private-ips` to block SSRF. Also set `--api-timeout` and disable routes you don't use.

### Is there a hosted alternative to running Gotenberg myself?

Yes. [html-pdf.com](https://html-pdf.com) runs Gotenberg for you behind a JSON API with `POST https://html-pdf.com/v1/pdf`. You get 100 free credits without a card, then pay from $0.005 per page, and failed renders cost nothing.
