> ## 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.

# Deploying on a PaaS

> Run mob on Render, Railway, or a compose-native platform.

The compose stack maps onto any container platform as four pieces:

* a small Caddy gateway that owns the public origin and routes paths
* the api, private
* the console, private
* a Postgres with the pgvector extension

The platform terminates TLS, so the gateway runs `deploy/paas`, a Caddy
that listens on the injected `PORT` with `auto_https off` and proxies `/mcp`,
`/auth`, `/api`, and `/.well-known` to the api and `/dashboard` to the
console over the private network.

Whatever the platform, `MOB_BASE_URL` must be the gateway's public origin.
It is the OAuth issuer, so attach your final domain before inviting members;
changing it later disconnects every agent.

## Render

The repo root contains a [Blueprint](https://render.com/docs/infrastructure-as-code)
(`render.yaml`) that defines the whole stack: the gateway as the public web
service, the api and console as private services, and a managed Postgres.
The api's first migration runs `CREATE EXTENSION IF NOT EXISTS vector`,
which Render permits on its managed databases.

<Steps>
  <Step title="Create your Discord and Slack apps">
    Follow [Discord setup](/discord-setup) and [Slack setup](/slack-setup).
    A deployment using one platform leaves the other platform's credentials
    blank.
  </Step>

  <Step title="Launch the Blueprint">
    In the Render dashboard, create a new Blueprint instance from your fork
    of this repository, or open
    [render.com/deploy?repo=https://github.com/promptrotator/mob.so](https://render.com/deploy?repo=https://github.com/promptrotator/mob.so).
    Render prompts for the credentials and generates `MOB_SECRET_KEY` and
    `MOB_CREDENTIAL_ENCRYPTION_KEY` itself.
  </Step>

  <Step title="Set the public origin">
    Attach your domain to the `mob-gateway` service (or keep its
    `onrender.com` URL), then set `MOB_BASE_URL` on `mob-api` to that origin.
  </Step>
</Steps>

## Railway

Railway [translates a compose file into one service per entry](https://docs.railway.com/guides/docker-compose)
rather than running it directly. Create a project with these services:

| Service    | Source                                   | Notes                                                                                                      |
| ---------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `gateway`  | this repo, root directory `deploy/paas`  | public service; attach your domain here                                                                    |
| `api`      | this repo, root directory `api`          | private                                                                                                    |
| `console`  | this repo, root directory `apps/console` | private                                                                                                    |
| `postgres` | image `pgvector/pgvector:pg17`           | volume on `/var/lib/postgresql/data`, `POSTGRES_USER`/`POSTGRES_PASSWORD`/`POSTGRES_DB` set to your values |

Railway's [private network](https://docs.railway.com/guides/private-networking)
is IPv6, and services are reachable at `<service>.railway.internal` on the
port the process binds. Two variables make the stack listen on IPv6:
`MOB_HOST=::` on the api and `HOSTNAME=::` on the console.

Variables to set:

* `gateway`: `MOB_API_UPSTREAM=api.railway.internal:8000`,
  `MOB_CONSOLE_UPSTREAM=console.railway.internal:3000`
* `api`: everything from `.env.example` except `CADDY_DOMAIN`, plus
  `MOB_HOST=::` and
  `MOB_DATABASE_URL=postgresql://<user>:<password>@postgres.railway.internal:5432/<db>`
* `console`: `HOSTNAME=::`

Railway's one-click artifact is a
[published template](https://docs.railway.com/templates/publish-and-share):
build the project once as above, publish it from the dashboard, and Railway
issues a template ID. The Deploy on Railway button then goes in the README:

```md theme={null}
[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/new/template/<TEMPLATE_ID>)
```

The template lives on railway.com rather than in the repository, so the
button only becomes possible after that one-time publish.

## Coolify, Dokploy, and other compose-native platforms

These platforms run the repository's `docker-compose.yml` as is: point them
at the repo and supply the `.env` values. The stack's Caddy expects to own
ports 80 and 443. When the platform's own proxy holds those ports, stop
publishing them and route the platform proxy at the `caddy` service instead,
with `CADDY_DOMAIN=localhost` so Caddy skips certificate fetching.
