Skip to main content
Self-hosting runs the same code as OpenNous Cloud, minus the private Cloud layer. See Cloud vs self-host for the differences. There are two ways to run it:
  • 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, api and mcp

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

In 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

This writes the database password, JWT secret, Supabase anon and service-role 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

The first build takes a few minutes. Caddy requests TLS certificates once DNS resolves. The app is at 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:
Open it and create the first account. It becomes the owner. A sign-up without the token is refused, even on an empty instance, so whoever reaches the page first cannot take it over. Lost the link? The token is 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

Set APP_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.
Every existing user is added to it as an admin; switch to it from the workspace menu. It refuses to load on a database with more than 5 users or 3 workspaces. Remove it with supabase/seed/demo-reset.sql.

Option 2: Bring your own Supabase

Use this if you already run Supabase, or want a managed database. It uses docker-compose.yaml, which runs api, worker, mcp, frontend and caddy. The images are built from source on your server.

1. Clone and configure

In nous.env, set:

2. Create the database

Required before first boot. Without it the app starts, but nothing works.
In your Supabase project, open SQL Editor and run the full contents of supabase/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 when OPENROUTER_API_KEY is set, else on ANTHROPIC_API_KEY.
  • Any other id runs on OpenRouter, or on the OpenAI-compatible endpoint LLM_BASE_URL names, keyed by LLM_API_KEY: OpenAI, Groq, Together, Fireworks, LiteLLM, or a local Ollama (LLM_BASE_URL=http://host.docker.internal:11434/v1, no key).
So one OpenRouter key runs everything with the defaults as they are. Settings → Instance setup shows which model runs each job, whether its key is set, and recommended setups to copy. Change the settings in 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 a npx @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

This signs you in against your instance, saves a key and the URL to ~/.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:
Remote MCP clients can use the hosted endpoint on your instance, 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.

Logs

Stopping

Back up the bundled database with:

Next