- Bundled stack (default). Everything runs in Docker on one server, including Postgres and auth. No Supabase account needed.
- Bring your own Supabase. The app containers run on your server and use a Supabase project for the database and auth.
Prerequisites
- A Linux server with Docker and Docker Compose v2.24 or newer
- Node.js 18+ on the server, to generate keys
- An AI provider key: one OpenRouter key runs every model (recommended), or an Anthropic key, or any OpenAI-compatible endpoint. See AI models.
- Three DNS records pointing at the server:
app,apiandmcp
Option 1: Bundled stack
docker-compose.selfhost.yaml sits on top of docker-compose.yaml and adds the database and auth. The bundled stack runs these services:
The app images are built from source on your server.
1. Clone and configure
nous.env, set APP_DOMAIN, API_DOMAIN, MCP_DOMAIN and OPENROUTER_API_KEY (or ANTHROPIC_API_KEY). Leave the Supabase values as they are; the next step replaces them.
2. Generate keys
ENCRYPTION_KEY and a one-time SELFHOST_SETUP_TOKEN into nous.env. It also sets COMPOSE_FILE so every docker compose --env-file nous.env command uses both compose files, and closes sign-up (DISABLE_SIGNUPS=true). It keeps values that are already set, so running it again is safe. --force regenerates everything; never use it on an install that has data. The file is left readable by its owner only.
The script refuses an env file that points at an external Supabase project or has Clerk keys set. Give the bundled stack its own file.
Every variable is listed in the environment reference.
3. Start
https://app.yourdomain.com and the API at https://api.yourdomain.com.
4. Create the owner account
The key script ends by printing the setup link:SELFHOST_SETUP_TOKEN in nous.env, and docker compose --env-file nous.env logs db-schema prints the link until an owner exists.
Try it on a laptop
SetAPP_DOMAIN=localhost, API_DOMAIN=api.localhost and MCP_DOMAIN=mcp.localhost. Caddy serves them with its own local certificate authority, so the browser warns and curl needs -k.
Demo data
To look around before connecting anything, load the fictional demo workspace, “Fernpoint (demo)”: 40 invented companies on.example domains, with people, deals, activity and claims.
supabase/seed/demo-reset.sql.
Option 2: Bring your own Supabase
Use this if you already run Supabase, or want a managed database. It usesdocker-compose.yaml, which runs api, worker, mcp, frontend and caddy. The images are built from source on your server.
1. Clone and configure
nous.env, set:
2. Create the database
In your Supabase project, open SQL Editor and run the full contents ofsupabase/schema.sql once. It is the complete schema, including the pgcrypto and vector extensions. A fresh install does not run the files in supabase/migrations/. Those are for upgrading.
3. Turn on email sign-in
In Supabase, open Authentication → Sign In / Providers and enable Email.4. Start
--env-file nous.env is needed: the frontend build and Caddy read their values from it. The first build takes a few minutes.
AI models
OpenNous runs its AI work on your own keys. Each job has its own model:- An id that starts with
claude-runs on OpenRouter whenOPENROUTER_API_KEYis set, else onANTHROPIC_API_KEY. - Any other id runs on OpenRouter, or on the OpenAI-compatible endpoint
LLM_BASE_URLnames, keyed byLLM_API_KEY: OpenAI, Groq, Together, Fireworks, LiteLLM, or a local Ollama (LLM_BASE_URL=http://host.docker.internal:11434/v1, no key).
nous.env, then run docker compose --env-file nous.env up -d.
A Claude Pro or Max subscription cannot run these jobs: Anthropic allows it only inside its own apps. Your agent in Claude Code does use it, since it talks to OpenNous over MCP.
Sign in and invite your team
Sign-in uses Supabase Auth with email and password. Bundled stack. The owner signs up through the setup link (step 4). After that the instance is closed: a login without an invitation gets no account, no workspace and nothing that spends your AI keys. To add a teammate, open Settings → Members → Create invite. It gives anpx @opennous/cli join <token> --url https://api.yourdomain.com command; the teammate runs it, creates a login on the page that opens, approves, and lands in your workspace. The token is the proof. Email invites are a Cloud feature.
Without SMTP, accounts are confirmed at sign-up and no mail is sent: email and password work, password reset and magic links do not. To send mail, set SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS and SMTP_ADMIN_EMAIL in nous.env, set GOTRUE_MAILER_AUTOCONFIRM=false, and run docker compose --env-file nous.env up -d. Without SMTP an email address proves nothing, so the API never hands anything over because an address matches; only invite tokens count.
Bring your own Supabase. The first account becomes the owner. To close public sign-up after that, set DISABLE_SIGNUPS=true and restart, and also turn off Allow new users to sign up in Supabase, since sign-up goes from the browser straight to Supabase.
Connect your agent
~/.nous/config.json, and registers the MCP server with your agent. The --url is what points it at your instance instead of Cloud.
To configure the MCP server by hand instead, create a key under Settings → API keys:
https://mcp.yourdomain.com/mcp, with the key as a Bearer header.
Integrations
Integrations are connected per workspace in Settings → Integrations, mostly with the provider’s own API key. A few need instance-level settings in the env file, for example LinkedIn (UNIPILE_API_KEY, UNIPILE_DSN) and Salesforce (SALESFORCE_CLIENT_ID, SALESFORCE_CLIENT_SECRET). See the environment reference.
Gmail and Google Calendar connect through your own Google OAuth app: set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET, and register https://api.yourdomain.com/api/oauth/google/callback on the client. The redirect is derived from API_DOMAIN. Composio is optional. See Gmail.