Guide

Install with Docker

Run Penweir on a single machine with Docker Compose or the plain Docker image, step by step.

This takes about ten minutes on a machine with Docker. Penweir runs as one container with a SQLite database on a volume. Postgres is optional. Running on Kubernetes? Use the Helm guide instead.

1. Get the demo

Download penweir-demo-1.3.0.zip from the download page and unpack it:

unzip penweir-demo-1.3.0.zip
cd penweir-demo-1.3.0

2. Create a master key

The master key encrypts stored provider keys and two-factor seeds. Keep it in your secret manager. A backup of the database is useless without it.

docker compose run --rm penweir keygen master

Put the key in a .env file next to compose.yaml, which Compose reads, and keep that file out of version control:

echo "PENWEIR_MASTER_KEY=<the key>" > .env
chmod 600 .env

An environment variable is visible to anyone who can run docker inspect on the host. Where that matters, keep the key in a file instead and mount it read-only: set PENWEIR_MASTER_KEY_FILE to its path inside the container, for example with a Compose secrets: entry, which appears under /run/secrets/. The Helm chart mounts it as a file by default.

3. Start Penweir and create the first admin

docker compose up -d
docker compose exec -it penweir penweir admin create <your-name>

There is no default account. The command asks for a password of at least 12 characters, and it refuses to run once an admin exists.

Check that it runs:

curl http://127.0.0.1:8080/readyz

From the machine itself, /readyz also says which check failed. Other callers only see ready or not_ready; the logs and the dashboard give the reason.

4. Configure it in the dashboard

Open http://127.0.0.1:8081 and sign in.

  1. Models, Add provider. For OpenAI: type openai_compatible, base URL https://api.openai.com/v1, and your API key. The key is stored encrypted.
  2. Pipelines, Edit configuration. Add a model and a pipeline, then choose Deploy changes:
models:
  - {name: cloud/default, targets: {openai: gpt-4o-mini}}

pipelines:
  - name: default
    default: true
    routing: {allow: [openai]}
    request_steps:
      - {detector: email, action: tokenize}
      - {detector: iban, action: tokenize}
      - {detector: bsn, action: block}

Then send your first request.

Without Compose

If you already have your own setup, run the image directly. Generate the key once and keep it, because the stored secrets cannot be read without it:

export PENWEIR_MASTER_KEY=$(docker run --rm ghcr.io/robinhana04/penweir-app:1.3.0 keygen master)
docker run -d --name penweir \
  -p 127.0.0.1:8080:8080 -p 127.0.0.1:8081:8081 \
  -v penweir-data:/var/lib/penweir \
  -e PENWEIR_MASTER_KEY \
  ghcr.io/robinhana04/penweir-app:1.3.0

Create the first admin with docker exec -it penweir penweir admin create <your-name>.

Exposing it to other machines

Both ports are published on 127.0.0.1 only. Before exposing them:

  • Terminate TLS in front of Penweir with a reverse proxy, or use the built-in TLS mode: PENWEIR_TLS_CERT_FILE and PENWEIR_TLS_KEY_FILE, which serve both ports over HTTPS and add HSTS to the admin port.
  • With a proxy, send Strict-Transport-Security: max-age=31536000 from it.
  • Keep the admin port (8081) on an internal network or VPN. Only applications need the gateway port (8080).
  • Restrict outbound traffic to the providers you configured.

Postgres

Set PENWEIR_DATABASE_URL=postgresql+asyncpg://user:password@host:5432/penweir. Penweir migrates the schema on startup. The database holds metadata and configuration only.

Upgrading

Unpack the new demo, then run docker compose up -d. Migrations run on startup. Back up the database volume first.

Removing it

docker compose down -v stops Penweir and deletes the volume, including the database.