Install
Paprika comes as one image and two files: compose.yaml, which runs the services, and .env.example, their settings. You need a machine with Docker and Docker Compose, and the image loaded into Docker or reachable in your registry.
Start it
Section titled “Start it”-
Copy the settings and change every secret in them: the database password,
PAPRIKA_ADMIN_TOKENandPAPRIKA_SIGNING_KEYS. SetPAPRIKA_IMAGEto the image you were given.Terminal window cp .env.example .envA long random value is a good secret:
openssl rand -base64 32makes one. -
Start the services and wait until every one is healthy:
Terminal window docker compose up --waitThe
migrateservice brings the database up to date first; the API starts after it. The editor is then athttp://localhost:4178and the API athttp://localhost:4179, both reachable from this machine only. -
Check the API is alive and ready. Ready means its storage is writable and PostgreSQL answers:
Terminal window export PAPRIKA_URL=http://localhost:4179curl "$PAPRIKA_URL/health"{ "status": "ok" }Terminal window curl "$PAPRIKA_URL/ready"{ "ready": true, "checks": { "storage": "ok", "database": "ok" } }When it isn’t ready the answer is 503 with what failed, in
checks.
Issue the first key
Section titled “Issue the first key”Each organisation using Paprika is a tenant, with its own templates, fonts, outputs and keys. Make one with the admin token from .env:
export PAPRIKA_ADMIN_TOKEN=…curl -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" -H "Content-Type: application/json" -d '{"id": "northwind", "name": "Northwind Traders"}' "$PAPRIKA_URL/v1/admin/tenants"{ "id": "northwind", "name": "Northwind Traders", "tier": null, "retentionDays": null, "createdAt": "2026-11-10T15:49:12.073Z", "suspendedAt": null, "deletedAt": null}Then give it a key with every scope, for the person who will make the organisation’s other keys:
curl -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" -H "Content-Type: application/json" -d '{"name": "integration", "scopes": ["render", "publish", "keys"]}' "$PAPRIKA_URL/v1/admin/tenants/northwind/keys"{ "key": "ppk_n0AAaSmCGft8_Jz6nXGhvrp9wmbfdskmFQI1hN5t8tqAUAsJA1tqF78k", "id": "n0AAaSmCGft8", "tenant": "northwind", "name": "integration", "scopes": ["render", "publish", "keys"], "createdAt": "2026-11-10T15:49:12.117Z", "expiresAt": null, "revokedAt": null, "lastUsedAt": null, "rotatedTo": null, "tier": null}The secret in key is shown only here; Paprika keeps a hash of it. Hand it over safely. With it, the organisation’s integrators can follow rendering from the API; a quick check that it works:
curl -H "Authorization: Bearer $PAPRIKA_KEY" "$PAPRIKA_URL/v1/templates"Tenants and API keys covers the rest of the admin API.
The editor’s tenant
Section titled “The editor’s tenant”The editor works for the built-in tenant local: the editor-key service issues its key. Designers using the editor publish into local, so give the integrators who render those templates keys of local too (/v1/admin/tenants/local/keys). One tenant per install is the usual self-hosted set-up.
Put it on your network
Section titled “Put it on your network”The Compose file publishes the editor and the API on 127.0.0.1 only. To serve them to others, put a reverse proxy that terminates HTTPS in front of them, passing on the host name the browser asked for, and tell each service its names in .env: PAPRIKA_EDITOR_HOSTS for the editor, PAPRIKA_API_HOSTS for the API, comma-separated.
PAPRIKA_EDITOR_HOSTS=paprika.example.comPAPRIKA_API_HOSTS=api.paprika.example.comA name alone is answered as the proxy passes it on, https://paprika.example.com included; a name with a port (paprika.internal:8443) only on that port. A request for any other host is refused, and so is a browser’s change sent from another site, which keeps out DNS rebinding and cross-site requests.
Keep /v1/admin off the internet: route it only from where you administer (security).
More workers
Section titled “More workers”The worker service renders jobs and batches; PAPRIKA_WORKERS sets how many it renders at once. For more, run more of them:
docker compose up --wait --scale worker=3A worker that stops mid-job leaves the job to another once its lease runs out, so stopping and replacing workers loses nothing.
Stop and start
Section titled “Stop and start”docker compose down stops the services and keeps the volumes: the database, the data and the editor’s key. docker compose down --volumes deletes them, and everything with them.