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.
- Models, Add provider. For OpenAI: type
openai_compatible, base URLhttps://api.openai.com/v1, and your API key. The key is stored encrypted. - 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_FILEandPENWEIR_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=31536000from 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.