One command
docker run -d --name bollwark -p 3000:3000 ghcr.io/hauju/bollwark:latestThat is a working service on port 3000 with every default in place — and it is
deliberately useless for anything but a smoke test, because without
ADMIN_TOKEN you cannot register a site. POST /v1/sites returns 404; there
is no anonymous provisioning.
The real minimum:
docker run -d --name bollwark -p 3000:3000 \
-e ADMIN_TOKEN="$(openssl rand -hex 32)" \
-e SITE_DB_PATH=/data/sites.db \
-v bollwark-data:/data \
ghcr.io/hauju/bollwark:latestCheck it with curl localhost:3000/healthz — it answers ok.
Compose
curl -O https://raw.githubusercontent.com/hauju/bollwark-api/main/docker-compose.yml
# uncomment ADMIN_TOKEN, and SITE_DB_PATH if sites should survive a restart
docker compose up -d
docker compose logs -fThe file in the repository is a single service with a named volume for /data
and every optional variable present but commented out. To build from a checkout
instead of pulling, swap image: for build: ..
The image
| Registry | ghcr.io/hauju/bollwark |
| Tags | latest, 1.2.3 / 1.2 / 1, sha-<commit> |
| Platforms | linux/amd64, linux/arm64 |
| Base | debian:trixie-slim |
| User | non-root, uid/gid 10001 |
| Port | 3000 |
| Data dir | /data |
Pin a version tag in production. latest moves on every push to main.
STATIC_DIR and LISTEN_ADDR are baked in and should not need overriding.
The two variables that matter
| Variable | Why |
|---|---|
ADMIN_TOKEN |
Gates POST /v1/sites and all of /v1/admin/*. Unset, those routes 404. Generate with openssl rand -hex 32. |
SITE_DB_PATH |
Without it, sites are in-memory only and every restart invalidates every integrator's secret_key. Set it to a path on your volume. |
Everything else is inert until configured: ADMIN_DB_PATH (the decision log
and validation dashboard — note it requires ADMIN_TOKEN or the server
refuses to start), GEOIP_DB_PATH, IP_REPUTATION_FILE,
TLS_FINGERPRINT_HEADER, LOAD_LADDER, PUZZLE_ALGORITHM and the difficulty
knobs, LOG_FORMAT=json.
All of them, with defaults and semantics, are in
CONFIGURATION.md.
Healthcheck
The image ships a HEALTHCHECK that curls /healthz. If you probe at the
orchestrator level instead, point it at GET /healthz — it returns 200 ok
and is the intended liveness probe.
It only proves the process is alive inside the container. It can't see a broken certificate on the proxy in front of it, which is what the next page is about.