Self-Hosting Guide
Run your own Thumper-Run node: your users, your repositories, your keys, on your hardware. A node is a small fleet of cooperating processes, not a single container — the end-to-end encryption root runs isolated from the web server on purpose. This page explains what a node is; the per-platform runbooks below carry the exact commands.
Prerequisites
- A Linux host — Ubuntu 24.04, Debian 12, Fedora 40 or newer; 4 vCPU, 8 GB RAM, 40 GB disk for the community node. macOS and Windows hosts work for development through Docker Desktop or WSL2.
- Docker Engine 24+ with Compose v2, or Podman 5 — until the single-binary
thumper-nodeinstaller ships. - A DNS name with ports 80 and 443 reachable — TLS is required for OIDC redirects and encrypted WebSocket sync.
- PostgreSQL 16 with the pgsodium extension — the
thumperai/postgres-pgsodiumimage, or your own instance with the extension installed. - S3-compatible object storage — a bucket at your provider, or the bundled store on the same host.
Editions & licence
The self-hosted community edition is free software under the GNU Affero General Public License v3. It includes end-to-end encryption, sign-in (OIDC, passkeys, browser sign-in key), repositories, the catalog, publishing, sync and federation. It does not include content moderation, upload screening, signed evidence (franking), security advisories or the audit anchor — those ship in the business and managed editions. Uploads and repositories on a community node are not screened; the operator is responsible for what the node hosts.
| Capability | Community | Business | Managed |
|---|---|---|---|
| End-to-end encrypted private projects (Mode-5) | Yes | Yes | Yes |
| OIDC sign-in, passkeys, browser sign-in key | Yes | Yes | Yes |
| Repositories, catalog, publishing, sync, federation | Yes | Yes | Yes |
| Moderation queue, DMCA, upload screening, ClamAV | No | Yes | Yes |
| Franking, security advisories, audit anchor | No | Yes | Yes |
| Billing, marketplace payouts, managed cloud wallet | No | No | Yes |
GET /version and GET /api/v1/client-config (the node block), and links to its own source code as the AGPL requires.Runbooks by platform
One runbook per platform, all with the same twelve steps (prerequisites, install, configure, first start, key ceremony, sign-in, verify, connect a client, upgrade, backup, uninstall, troubleshooting). They live in the repository under docs/runbooks/selfhost/.
| Platform | Install path | Runbook |
|---|---|---|
| Linux with Docker Compose or Podman | compose | LINUX_DOCKER_COMPOSE.md |
| Linux with systemd (single-binary installer) | thumper-node bundle | LINUX_SYSTEMD.md |
| macOS (Docker Desktop, OrbStack) | compose | MACOS_DOCKER_DESKTOP.md |
| Windows 11 (WSL2) | compose or bundle in WSL2 | WINDOWS_WSL2.md |
| Kubernetes (k3s, kind, managed) | plain manifests | KUBERNETES.md |
| Raspberry Pi 5 and arm64 boards | bundle or compose | RASPBERRY_PI_ARM64.md |
| NAS (Synology, Unraid, TrueNAS SCALE) | compose via the appliance UI | NAS_SYNOLOGY_UNRAID.md |
| Cloud VPS (Hetzner, DigitalOcean, Vultr, Lightsail, GCP, Azure) | bundle | CLOUD_VPS.md |
| Air-gapped | offline bundle | AIRGAPPED.md |
| Upgrade and rollback / Backup and restore | all | UPGRADE_AND_ROLLBACK.md, BACKUP_RESTORE.md |
docker/docker-compose.mode5.yml and marks the steps that still depend on unshipped pieces.What a node runs
The web server never holds the encryption root or the session-signing root key. Those live in separate processes with their own users and sockets; the root broker has no network at all. This is what a community node runs.
| Service | Image | Purpose |
|---|---|---|
| web | thumperai/tr-web-shell (community build) | SSR app, API, sync relay |
| migrate | thumperai/tr-db-migrate (same tag) | schema migrations; runs before the web on every upgrade |
| postgres | thumperai/postgres-pgsodium | database with pgsodium |
| rauthy | ghcr.io/sebadob/rauthy (pinned) | OIDC identity provider |
| objstore, lakefs | rustfs or external S3; lakeFS | uploads, screenshots, repository blobs |
| general-biscuit-signer | thumperai/general-biscuit-signer | session and capability token signing |
| mode5-root-broker, field-key-issuer, recipient-registrar | thumperai/tr-mode5-* | end-to-end encryption root and field keys (broker: no network, local state) |
| account-* | thumperai/tr-account-* | sign-in binding, browser sign-in key, E2EE account enrollment |
| caddy | caddy | TLS, static caching, WebSocket upgrade |
| screenshot-renderer (optional) | browserless/chrome | listing screenshot capture |
Environment Variables
The web server reads about a hundred variables; the complete list with tiers (required, recommended, advanced, business-only, managed-only) is generated from the source into docs/generated/env.md and .env.example, and a contract test keeps them in sync. These are the ones a community node cannot boot or sign in without.
| Variable | Purpose |
|---|---|
| TR_EDITION | self-hosted-community — must match the compiled edition or the server refuses to start |
| TR_PUBLIC_URL | the origin your users open; canonical URLs, sign-in redirects and the node identity derive from it |
| TR_NODE_NAME | name shown in clients' node picker and the connection popover |
| DATABASE_URL, MIGRATION_DATABASE_URL | runtime and migration-owner roles (separate credentials) |
| RAUTHY_URL, RAUTHY_CLIENT_ID, RAUTHY_REDIRECT_URI | OIDC provider and the client registered for this node |
| S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY (+ secret file), S3_REGION | object storage |
| TR_ENROLL_CHALLENGE, TR_VALIDATE_JWT_AUD | boot secrets; the server refuses to start without them |
| RELAY_ADMIN_SECRET, INITIAL_RELAY_ADMIN_EMAIL | operator console secret and the first operator account |
| TR_SINGLE_USER_E2EE_ENABLED, TR_ORG_E2EE_ENABLED, TR_PRIVATE_E2EE_ENABLED | turn on end-to-end encrypted projects (the Mode-5 fleet must be running) |
SESSION_SECRET. Secrets belong in files referenced by the compose secrets: block, never inline in .env.SSL Certificates
SSL is required for OIDC redirects and secure WebSocket sync. Choose your method.
The easiest option. Use Caddy or Certbot as a reverse proxy.
# Install Caddysudo apt install caddy# Caddyfile (auto-provisions TLS)thumper.example.com {reverse_proxy localhost:3000}
Caddy automatically provisions and renews Let’s Encrypt certificates.
OIDC (Rauthy)
Thumper-Run uses Rauthy as its OIDC identity provider. Any OIDC provider that issues the expected claims should work; Rauthy is the one we test.
Initial Setup
# Provision the OIDC client for Thumper-Runpython3 scripts/rauthy-provision-client.py \--rauthy-url http://localhost:8080 \--client-name "Thumper Run" \--redirect-uri https://thumper.example.com/auth/callback
The script prints the client id for RAUTHY_CLIENT_ID and writes the client secret; keep the secret in a file referenced by the compose secrets block.
TR_PUBLIC_URL + /auth/callback. Never commit client secrets to version control.Monitoring
Logs
View logs from all services.
# All servicesdocker compose -f docker/docker-compose.mode5.yml logs -f# Web server onlydocker compose -f docker/docker-compose.mode5.yml logs -f web# Set log level via RUST_LOGRUST_LOG=debug docker compose -f docker/docker-compose.mode5.yml up web
Health Check
The web server exposes a health endpoint.
curl -fsS https://node.example.org/readyzcurl -fsS https://node.example.org/version | jq .# {"edition": "self-hosted-community", "api_version": 1, "source_url": "...", ...}curl -fsS https://node.example.org/api/v1/client-config | jq .node
Updating
A migration is a hard dependency: the migrator from the new release must finish before the new web starts. Migrations are forward-only; rollback means the previous web binaries against the current schema.
git fetch --tags && git checkout <new tag>docker compose -f docker/docker-compose.mode5.yml pull# 1. migrate first — must exit 0docker compose -f docker/docker-compose.mode5.yml up migrate# 2. then the web and sidecarsdocker compose -f docker/docker-compose.mode5.yml up -d web# 3. verifycurl -fsS https://node.example.org/version && curl -fsS https://node.example.org/readyz
Full procedure, including the single-binary installer and Kubernetes: docs/runbooks/selfhost/UPGRADE_AND_ROLLBACK.md.
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| 502 Bad Gateway | Web server not ready | Wait 30s for startup; check docker logs web |
| OIDC redirect error | Mismatched redirect URI | RAUTHY_REDIRECT_URI and the Rauthy client must both be TR_PUBLIC_URL + /auth/callback |
| DB connection refused | Postgres not started | Check docker ps for the postgres container; verify DATABASE_URL and that pgsodium loaded |
| WebSocket fails | Missing proxy upgrade | Add WebSocket upgrade headers in nginx/Caddy config |
| SSL certificate error | DNS not propagated | Verify DNS with dig thumper.example.com; wait for propagation |
| Server exits: TR_EDITION does not match the compiled edition | Image built for another edition | Use the community image, or fix TR_EDITION |
| E2EE project cannot be created | Mode-5 fleet not running or issuer socket owned by root | Re-run the ceremony steps; check client-config.node.e2ee |
Key Takeaways
- A node is a small fleet: web, migrator, Postgres with pgsodium, Rauthy, object storage, the biscuit signer, the Mode-5 encryption processes and the account services
- Community edition = AGPL-3.0, with E2EE and sign-in, without moderation, screening, franking or advisories — uploads and repos are unscreened
- Set TR_EDITION, TR_PUBLIC_URL, TR_NODE_NAME, the database, Rauthy and S3 variables, and the two boot secrets
- Export the encryption root key off the host during the ceremony; losing it loses every E2EE project
- Upgrade = migrate first, then the web; migrations are forward-only
- Every node publishes its edition, screening status and source link at /version and /api/v1/client-config
Advanced Reading
The full plan, edition boundary and the single-binary installer design: docs/plans/SELF_HOSTED_NODE_PLAN_2026-09-13.md; licensing: docs/legal/LICENSING.md.