Run Manifest on your own machine with Docker. Covers the quick installer, docker-compose, bringing your own PostgreSQL, signed images, and telemetry opt-out.
Run the full Manifest stack on your own machine. No Node.js required, just Docker.All three paths end in the same place: a running stack at http://localhost:2099 where you sign up. The first account you create becomes the admin. No demo credentials are pre-seeded.
The bundled compose file binds port 2099 to 127.0.0.1 only, so the dashboard is reachable on the host machine but not over the LAN. See Exposing on the LAN to change this.
One command. The installer downloads the compose file into ~/manifest, generates a secret, and brings up the stack. First boot pulls the app image and Postgres, so give it up to a couple of minutes.
Useful flags: --dir <path> to install elsewhere, --dry-run to preview, --yes to skip the confirmation prompt.When the installer finishes, open http://localhost:2099 and sign up for an account. Then head to the Routing page to add an LLM provider (OpenAI, Anthropic, Gemini, etc.) with your API key.
Same underlying flow as the install script, but you drive it yourself so you can edit the config before booting the stack.
Open .env in your editor and set BETTER_AUTH_SECRET to a random string. You can generate one with:
openssl rand -hex 32
Optional: to use a stronger database password, set both POSTGRES_PASSWORD and DATABASE_URL in .env — they must agree, and any special characters in the password need to be percent-encoded in the URL.
3
Start the stack
docker compose up -d
Give it up to a couple of minutes on a cold pull — you can watch startup with docker compose logs -f manifest.
4
Create your admin account
Go to http://localhost:2099 and sign up. The first account you create becomes the admin.
5
Connect a provider
Open the Routing page and add an LLM provider (OpenAI, Anthropic, Gemini, etc.) with your API key.
Before exposing this instance beyond localhost, double-check that BETTER_AUTH_SECRET is a real secret (not the placeholder), and if you enable email verification, set BETTER_AUTH_URL to a reachable public URL so the verification links resolve.
If you already have a PostgreSQL instance, replace user, pass, and host with your actual database credentials:
After connecting a provider, send a test request and watch it land in the dashboard. Grab your Manifest API key from the dashboard (it starts with mnfst_) and run:
If the response comes back with That doesn't look like a Manifest key, you’re still using the placeholder — replace mnfst_YOUR_KEY_HERE with the real key from the dashboard.
If you see an “Invalid origin” error on the login page, BETTER_AUTH_URL doesn’t match the URL you’re accessing the dashboard on. The host matters as much as the port.
Upgrading from a pre-2099 install? Your existing stack keeps running on port 3001 with no changes — the backend’s own fallback is still 3001, so the new image works against your old compose file. If you want to refresh your compose file but stay on the legacy port (to avoid reconfiguring OAuth callbacks, reverse proxies, or bookmarks), set PORT=3001 in .env and the bundled compose file will honour it for both the host binding and the internal listener.
By default the compose file binds port 2099 to 127.0.0.1 only. The dashboard is reachable from the host but not from other machines on the network. To expose it on the LAN:
1
Change the port binding
Edit docker-compose.yml and change the ports line from "127.0.0.1:2099:2099" to "2099:2099".
2
Set BETTER_AUTH_URL
In .env, set BETTER_AUTH_URL to the host you’ll reach the dashboard on, e.g. http://192.168.1.20:2099 or https://manifest.mydomain.com. This must match the URL in the browser or Better Auth will reject the login with “Invalid origin”.
Manifest ships a new image on every release. To upgrade an existing compose install:
docker compose pulldocker compose up -d
Database migrations run automatically on boot, no manual steps. Your data in the pgdata volume is preserved across upgrades. Pin to a specific major version (e.g. manifestdotbuild/manifest:5) in docker-compose.yml if you want control over when major upgrades happen.
All state lives in the pgdata named volume mounted at /var/lib/postgresql/data in the postgres service. Nothing else in the Manifest container is stateful.Back up (from the host, with the stack running):
Default: 100 requests per 60-second window.LLM proxy
Variable
Default
Description
PROVIDER_TIMEOUT_MS
180000
Per-attempt timeout (ms) for upstream provider requests. Set strictly below your client’s timeout so the fallback chain has room to run. Slow local models may need this raised. Non-numeric, zero, or negative values fall back to the default.
Once a day, each install sends us a small anonymous report. That’s how we know whether anyone’s actually using the thing, and which providers are popular enough to deserve more work. It’s aggregates, never content: no prompts, no messages, no keys, nothing tied to a user. Fifteen fields total.
So the shape can grow without breaking old clients. Stays 1 for additive changes; bumps on breaking ones
install_id
random UUIDv4
Count distinct installs. Generated once on first boot, persisted, never rotated
manifest_version
5.47.0
Version adoption across the fleet
messages_total
1284
Daily activity per install
messages_by_provider
{"anthropic": 700, "openai": 500}
Provider mix. Anything we don’t recognize collapses to "custom", so self-hosted provider names and URLs stay local
messages_by_tier
{"simple": 800, "standard": 400, ...}
Routing tier usage
messages_by_auth_type
{"api_key": 1200, "subscription": 84}
API key vs. paid-subscription usage
tokens_input_total
1_450_000
Volume-weighted signal
tokens_output_total
890_000
Same
cost_usd_total†
47.83
Sum of cost_usd Manifest computed at routing time, rounded to cents. Lets us see real dollar throughput instead of guessing from token counts. 0 for Ollama-only / free-API installs
cost_usd_by_provider†
{"anthropic": 30.50, "openai": 17.33}
Per-provider split of cost_usd_total, rounded to cents. Same "custom" collapse rule as messages_by_provider — admin-configured BYOK pricing is never keyed by the raw provider name
agents_total
4
Configuration scale
agents_by_platform
{"openclaw": 3, "hermes": 1}
Which agent clients people use
platform
linux / darwin / windows
OS distribution
arch
x64 / arm64
Architecture distribution
†Optional. Installs running older Manifest versions omit these fields; receivers should feature-detect on presence rather than on schema_version. Cost values are derived from the same input_tokens / output_tokens we already ship, multiplied by Manifest’s per-model pricing table — no new data leaves the box, just a rolled-up dollar figure for what’s already disclosed.
Tenant IDs, user IDs, emails, API keys, prompts, message contents, model names, custom provider URLs, OAuth client IDs, hostnames, raw IPs. The ingest takes a SHA-256 of your IP and throws the original away; we keep the hash so we can rate-limit bad actors without knowing where they actually live.