BlogTutorial

How to Build a Self-Hosted Screenshot API with Docker

Deploy in minutes. No API key. No rate limits.

What you'll build: A running REST API that converts HTML snippets, uploaded HTML files, or website URLs to PNG screenshots — deployed on your own server with Docker Compose, using Openkova (MIT-licensed, open source).

Why self-host a screenshot API?

Most screenshot services charge per request. At low volumes this is fine. But as soon as you add CI visual regression tests, automated report generation, or an AI agent that needs to observe web pages, the bill grows fast.

A self-hosted screenshot API solves three problems at once:

Prerequisites

Step 1: Clone and start

git clone https://github.com/scnix-git/openkova
cd openkova
docker compose up

Docker pulls the pre-built image, starts the container, and exposes port 3000. The first start takes 30–60 seconds while Chromium initialises. You should see:

▲ Next.js ready on http://0.0.0.0:3000

Step 2: Test with a URL

Open a second terminal and send your first screenshot request. The -N flag keeps the connection open to read the SSE stream:

curl -N -X POST http://localhost:3000/api/convert/url \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "depth": 1}'

You'll receive Server-Sent Events with progress updates, then a final data: {"type":"done","filePath":"/data/..."} event. The PNG is saved to the container's /data directory.

Step 3: Convert an HTML snippet

For report generation or OG images, send raw HTML directly — no URL required:

curl -N -X POST http://localhost:3000/api/convert/snippet \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<div style=\"background:#7c6af7;color:white;padding:48px;font-family:sans-serif;font-size:2rem\">Hello, Openkova</div>"
  }'

Step 4: Upload an HTML file

If your template lives on disk, use the multipart upload endpoint:

curl -N -X POST http://localhost:3000/api/convert/file \
  -F "file=@./invoice.html"

API reference summary

EndpointContent-TypeBody
POST /api/convert/urlapplication/json{"url":"...","depth":1}
POST /api/convert/snippetapplication/json{"html":"...}
POST /api/convert/filemultipart/form-datafile=@path/to/file.html

All three endpoints return an SSE stream. See the API reference for the full event format.

Deploying to a VPS or cloud platform

To deploy outside your laptop, copy the docker-compose.yml to your server and run docker compose up -d. Set these environment variables in your platform dashboard or a .env file:

CHROMIUM_PATH=/usr/bin/chromium
OPENKOVA_STORAGE_PATH=/data

On Railway, Render, or Fly.io, the Dockerfile handles Chromium installation automatically — just point to the repository and deploy. Railway with a Dockerfile builder works reliably because Chromium installs from Debian apt (not snap).

Production considerations

Frequently asked questions

What is a self-hosted screenshot API?

A web service you run on your own infrastructure — on a VPS, Docker host, or cloud container — that converts HTML or URLs to PNG. No SaaS account, no API key, traffic stays on your servers.

Does Openkova require an API key?

No. It is MIT-licensed open-source software. There are no usage fees, no rate limits, and no registration required.

How does Openkova handle Chromium in Docker?

The Docker image uses node:24-slim (Debian) and installs Chromium via apt-get. This gives a real binary at /usr/bin/chromium. The CHROMIUM_PATH env var tells Openkova where to find it.

Can I deploy Openkova to Railway, Fly.io, or Render?

Yes. Any platform supporting Docker deployment works. Set CHROMIUM_PATH=/usr/bin/chromium and OPENKOVA_STORAGE_PATH to a writable path. The Dockerfile installs Chromium from apt automatically.

Compare Openkova with Urlbox, html2canvas, and wkhtmltopdf, or read about using it as a screenshot API for AI agents.