> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mob.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Self-hosting

> Run your own mob instance with Docker Compose.

Your instance is the same artifact that runs the hosted mob.so, configured
entirely through one `.env` file. This page covers a machine you control;
[Deploying on a PaaS](/deploy-paas) covers Render, Railway, and
compose-native platforms.

<Steps>
  <Step title="Point your domain's DNS at the machine that will run mob" />

  <Step title="Create your Discord app">
    Follow [Set up your Discord app](/discord-setup).
  </Step>

  <Step title="Create your Slack app">
    Follow [Set up your Slack app](/slack-setup). A deployment using one
    platform leaves the other platform's credentials empty.
  </Step>

  <Step title="Get the code">
    `git clone https://github.com/promptrotator/mob.so.git && cd mob.so`
  </Step>

  <Step title="Configure">
    `cp .env.example .env` in the repo root and work through it; every
    value is explained inline.
  </Step>

  <Step title="Start the stack">
    `docker compose up -d`
  </Step>
</Steps>

Your instance is now at `https://<your-domain>/dashboard` (the root
redirects there).

* Caddy fetches TLS certificates for your domain automatically.
* Ports 80 and 443 handle public ingress.
* Discord uses an outbound gateway connection.
* Slack sends signed HTTPS Events API and slash-command requests to
  `/api/slack/*`.

## Trying the stack locally first

The compose stack runs on a laptop before any DNS exists. In `.env`, set
both origins to plain local HTTP; the scheme prefix on `CADDY_DOMAIN` is
what tells Caddy to skip TLS and the HTTPS redirect:

```bash theme={null}
MOB_BASE_URL=http://localhost
CADDY_DOMAIN=http://localhost
```

Then `docker compose up -d` and open `http://localhost/dashboard`; the MCP
endpoint answers at `http://localhost/mcp`. A loopback origin is also the
one place the api starts without real secrets, so an otherwise empty `.env`
works for this trial. Platform credentials stay optional: each adapter
stays idle until its credentials exist.

## Database

The stack includes Postgres with pgvector. It stores:

* identity and policy metadata
* encrypted Slack OAuth credentials
* the Discord message index

Slack query responses remain in request memory until the MCP response
completes.

Use encrypted storage for the Postgres volume. A managed database deployment
sets `MOB_DATABASE_URL` in `.env` and removes the `postgres` service from the
compose file. The database must have the pgvector extension.

The volume survives container rebuilds and goes down with the machine.
[Backups](/backups) covers what an instance loses with it and how to
restore.

## Model endpoint

`MOB_OPENAI_API_KEY` and `MOB_OPENAI_BASE_URL` point the instance at one
OpenAI-compatible endpoint. OpenAI itself, an Azure deployment, and a
self-hosted server all work. The endpoint serves three features, and the
instance runs without any of them when the key stays empty.

| Setting               | Default                  | Requirement                          |
| --------------------- | ------------------------ | ------------------------------------ |
| `MOB_EMBEDDING_MODEL` | `text-embedding-3-small` | Must produce 1536-dimension vectors. |
| `MOB_SMALL_MODEL`     | `gpt-4o-mini`            | Must be vision-capable.              |

**Semantic search.** Discord full-text search works with the local database
alone. The key adds an embedding leg to every Discord query, using
`MOB_EMBEDDING_MODEL`.

**Attachment contents.** Text, CSV, and PDF attachments on Discord messages
are extracted into the search index directly, with no model call and no key.
Images additionally require the key: `MOB_SMALL_MODEL` transcribes and
describes each image into the index, capped per community per day. Every
attachment is searchable by filename regardless.

**The security guard.** The same small model runs the optional pre-post
screen described in [Authorization](/authorization#security-guard). The
instance supplies only the endpoint; each community owner decides in the
console whether their server uses it and with what policy. Enabling it
without a configured key is rejected.

The endpoint handles Discord content only. Slack search calls
`assistant.search.context` with the requesting member's token and keeps the
response in request memory through the MCP response.

## Connecting agents to your instance

Members connect through the platform's add-by-URL mechanism. A custom
connector in Claude or ChatGPT points at `https://<your-domain>/mcp`.
Directory listings in the MCP clients point at hosted mob.so, and so does
the public communities page, which ships with the marketing site in the
hosted profile. The connect page reached from `/mob connect` shows the
current instance URL.
