Installing Hivemind
This is the walkthrough for a fresh install of self-hosted Hivemind. Read it once end-to-end before running anything — there are four prerequisites + one copy-paste command + a short verification step. Plain text, copy-pasteable. American English. The bootstrap path delivers a per-customer signed bundle; the install script generates host secrets locally and never carries any of them over the wire.
Prerequisites
Hivemind runs as a small Compose stack (one server image + a Postgres). You need:
| Requirement | Why | Verify |
|---|---|---|
| Linux host (x86_64) | The published image is glibc / amd64. | uname -m shows x86_64. |
| Docker Engine 24+ and Docker Compose v2 | The stack uses Compose's secrets: + per-service healthchecks. |
docker --version, docker compose version. |
Outbound HTTPS to updates.seglamater.app and registry.seglamater.app |
The bootstrap fetches your bundle + the signed image. | curl -fsS https://updates.seglamater.app/.well-known/keys/hivemind.pub returns a PEM. |
| ~2 GB free disk + ~512 MB RAM | Container + database + room for migrations. | df -h /srv and free -h. |
| A user with permission to write to your chosen install directory | The default is /srv/hivemind/. Use any path you own. |
mkdir -p /srv/hivemind && touch /srv/hivemind/.testwrite && rm /srv/hivemind/.testwrite |
Optional, only if you want them:
- An LLM API key if you want agents to answer (Anthropic, OpenAI, Ollama,
or any OpenAI-compatible endpoint). Hivemind boots and runs without one;
LLM-dependent surfaces report
LLM startup ping FAILEDand stay degraded until you configure a provider in.env(seebyok-llm.md). - A reverse proxy (Caddy, Traefik, nginx) if you'll terminate TLS in
front of Hivemind. The container binds
127.0.0.1:8585by default — that is intentional so the unauthenticated API isn't exposed on every interface during install. See the operator notes for a Caddy snippet. - An OIDC IdP (Authentik / Keycloak / similar) if you want SSO. The
default is local accounts; see
sso-setup.mdto switch.
Install
You should have received an invite URL from your Seglamater contact. It
looks like https://s.seglamater.app/i/<invite-id>. Run:
That bootstrap script:
- Fetches the bundle for your customer (
bundle.json) and the top-levelinstall.sh. - Pins the install layout under
$HIVEMIND_INSTALL_DIR(default/srv/hivemind/). Override before piping withHIVEMIND_INSTALL_DIR=/path/you/own curl … | bash. - Invokes
install.shwith the bundle. The bundle tellsinstall.shwhich image digest and which channel to pin, plus yourclient_id, the public URL you should use, and a few customer-specific defaults.
You'll see a sequence of [install] lines as the script:
- Verifies your bundle's signature against the pinned cosign public key.
- Pulls the digest-pinned
hivemind-serverimage fromregistry.seglamater.app/seglamater/hivemind-server@sha256:…and the Postgres image. - Generates per-host secrets (database password, admin API key, JWT
signing key, cookie secret, the integration encryption key for chatalot
bot tokens) into
./secrets/under your install dir, mode0600. These are written locally — they never leave your host and they are NOT in the bundle. - Renders
.envfrom defaults and the bundle metadata. - Brings up the Compose stack and waits for the server healthcheck to pass.
Expected total runtime: 1-3 minutes on a warm Docker cache, 3-5 minutes for the first pull on a clean host.
The script is idempotent. Re-running it picks up an existing install: it
reuses existing secrets (does not regenerate them), re-renders .env
only for keys it doesn't already see, and a docker compose up -d brings
the stack back to running. If you need a true fresh install, remove the
install dir and start over.
First run
After install.sh exits success, you should see one of these on the host:
curl -fsS http://localhost:8585/api/v1/health
# {"status":"ok","version":"0.1.9"}
docker compose ps
# All three of hivemind-server, hivemind-db (postgres), hivemind-updater
# should show "Up" and "(healthy)".
If /api/v1/health returns ok, Hivemind is running. The unauthenticated
API is intentionally bound to 127.0.0.1:8585 — to expose it on the
internet you need a reverse proxy in front of it; see operator-notes.md
for a Caddy snippet that terminates TLS, sets the right Host header, and
forwards /api/v1/* to the container.
Logs live in the container; tail them with:
cd /srv/hivemind # or your install dir
docker compose logs --tail=100 hivemind-server
docker compose logs --tail=100 postgres
The first time the server starts, look for these specific lines (they're the load-bearing init steps and they each name what's wrong if they fail):
database migrations appliedBYOK LLM provider initialised …(the line below it may be a WARN about the LLM ping failing — that's expected until you setHIVEMIND_LLM_API_KEYin.env, seebyok-llm.md).integration credential key loaded — chatalot integrations enabledlistening addr=0.0.0.0:3000
Admin setup
The first POST to /auth/register becomes the admin account. After install,
do this once (only allowed while the users table has no local accounts —
the second POST 409s):
curl -fsS -X POST -H 'Content-Type: application/json' \
-d '{"email":"you@example.com","password":"<strong password>"}' \
http://localhost:8585/auth/register
Or, if you prefer the web UI: open the URL from your .env's
HIVEMIND_PUBLIC_URL (set this before you enable any operator-facing
surface) and register from the form on the home page. The first registered
user is the admin.
For service-to-service authentication (your other tools calling Hivemind),
use the ADMIN_API_KEY from ./secrets/admin_api_key as the X-API-Key
header. The local admin account is for browsers; the API key is for
daemons.
If you'll switch to SSO instead of local accounts, do that before
registering — see sso-setup.md. With forward auth enabled, the
POST /auth/register path is closed and identities come from your IdP.
What lives where
<install-dir>/ (default /srv/hivemind)
├── docker-compose.yml # The stack. Don't edit; rerun install.sh to upgrade.
├── docker-compose.override.yml # (optional) Your local additions: reverse-proxy labels,
│ # extra networks, host-specific extra_hosts. Survives
│ # upgrades — install.sh never touches it.
├── .env # Configuration. Edit to set LLM keys, OIDC, public URL.
├── scripts/
│ ├── install.sh # Bootstrap also dropped this here; run with --help.
│ └── bundle.json # Your signed bundle. Pins image digest + channel.
├── secrets/ # 0700 dir, 0600 files. Local-only, never shipped.
│ ├── db_password
│ ├── admin_api_key
│ ├── jwt_signing_key
│ ├── cookie_secret
│ ├── integration_encryption_key # ChaCha20-Poly1305 key for chatalot bot tokens at rest
│ ├── updater_token # HMAC for the in-process updater sidecar
│ └── cosign_pub # Pinned cosign pubkey (does not rotate without a release)
└── (a `postgres-data` Docker volume, not a host path)
The postgres data is a Docker named volume (hivemind_pgdata), not a
host bind mount. Back it up via docker compose exec postgres pg_dump … or
with Borg / Restic against /var/lib/docker/volumes/hivemind_pgdata/.
Common follow-ups
- Configure your LLM provider (the boot log will be loud until you do)
— see
byok-llm.md. - Set the public URL in
.env(HIVEMIND_PUBLIC_URL=https://hivemind.example.com) before you put a reverse proxy in front. - Switch to SSO — see
sso-setup.md. - Apply updates — managed-update path detects new signed releases and
applies them on operator approval. See
upgrade.md.
If anything in the install errored, the install dir is left in place for
inspection (no auto-teardown on failure). Re-run install.sh after fixing
the cause; it's idempotent.
If the post-install verifications above don't pass, see troubleshooting.md
— it has the boot-time failure modes and their fixes, including the most
common one ("integration routes return 503 integration_key_missing" — almost
always a .env not loading or secrets/ not mounted).