Self-host Dots

Run Dots with one command: bundled Postgres, bundled object store, in-process identity plane, and secrets generated on first boot.

The supported self-host path is the committed self-contained stack. It runs the web app, API, worker, Postgres, an object store, and the in-process identity plane without an external identity vendor, a storage account, or a secret manager. File uploads work out of the box.

If you want an instance running today rather than the full reference, Quickstart: Self-hosting Dots is the short route through the same stack.

1. Start The Stack

No configuration is required. From the repository root:

SH
docker compose -f deploy/self-contained/docker-compose.yml up --build

On first boot a one-shot secrets service generates the database password, the session-signing secret, the workspace company id, and the object store's credentials into a persistent volume, and the rest of the stack sources them. They are generated once and never rotated when containers are recreated, so there is nothing for you to record or rotate. A one-shot init service then applies migrations and provisions the storage bucket.

Open http://localhost:3000 and sign up. The first signup becomes the owner and provisions the workspace — no owner credentials ship with the stack, and none need to be set.

2. Configure Only What You Want To Change

Everything above works with no env file. To add a chat provider or override a default, copy deploy/self-contained/.env.selfcontained.example to .env beside the compose file; anything you set there wins over the generated defaults. Never commit the filled-in .env.

The one value most evaluators set is a chat provider — OPENROUTER_API_KEY or OPENCLAW_GATEWAY_URL. Without one the stack still boots, still passes its health probe, and still serves login; only conversations degrade until a provider is set.

PUBLIC_APP_URL defaults to http://localhost:3000. Plain HTTP is accepted for loopback hosts only, so a local evaluation needs no TLS; serving the stack at a real hostname requires setting it to an https:// origin.

3. Verify The Instance

  1. Open http://localhost:3000/api/v1/healthz and confirm a successful response.
  2. Sign in through the Dots login page. The session is issued and verified by the in-process identity plane.
  3. Create a Dotabase with a File property, upload a small file, and reopen it. The persisted URL lasts six days; reads mint a fresh one-hour URL from the durable object path.
  4. Confirm the worker remains healthy and background work continues after the first request.

For Railway, use deploy/self-contained/railway.json. It declares the same app, worker, and bundled-Postgres capability, but it is bring-your-own storage and brings no object store of its own: set the secret-bearing variables — including the owner credentials and the DOTS_S3_* set if you want uploads — in the Railway dashboard; do not commit them.

Connectors: Bring Your Own Key

Connectors — notion, github, fathom, fireflies — are optional and bring-your-own. No connector key ships with the stack and none is relayed on your behalf, so with none configured the instance still boots, serves, and onboards exactly as above. With no key configured, the Integrations surface reports no configured key and a gateway call returns COMPOSIO_GATEWAY_UNAVAILABLE. Nothing else is affected.

There are two ways to supply a key, and on a self-hosted instance the per-user path is the better default:

  • A personal key — recommended, no server configuration. Each person saves their own Composio API key under Settings → Workspace → Integrations. A personal key overrides the deployment key for that person, so their connections run under their own Composio account. This works on an instance that has no deployment key at all, which is why nothing in .env is required to make connectors usable.
  • A deployment-wide key. Set COMPOSIO_API_KEY in .env to give everyone a shared default that a personal key still overrides per person. It does not stand alone: with it set, the four Auth Config ids from the same Composio project — COMPOSIO_AUTH_CONFIG_NOTION, COMPOSIO_AUTH_CONFIG_GITHUB, COMPOSIO_AUTH_CONFIG_FATHOM, and COMPOSIO_AUTH_CONFIG_FIREFLIES — are required too, and the app refuses to start while any of them is missing or blank, naming the ones it needs.

Bring Your Own Infrastructure

You may replace bundled Postgres or storage independently:

  • Managed Postgres: supply DATABASE_URL and DIRECT_URL, keep DOTS_RAW_POSTGRES=0, and preserve the provider's TLS requirements. DOTS_RAW_POSTGRES=1 is an explicit capability declaration for the bundled plain-Postgres service; unset or 0 keeps init from installing the raw-Postgres compatibility schema or disabling TLS.
  • S3-compatible storage: point the complete DOTS_S3_* set at your own endpoint instead of the bundled object store. DOTS_STORAGE_DRIVER=disk also accepts uploads, but stores them on the app process's local filesystem; container-local files are ephemeral and separate app/worker services do not share them.
  • SMTP and social sign-in are optional additions. Core email/password identity remains in-process.

Keep migrations and init inside a protected deployment job, verify the target connection before running them, and fail the deployment if init or the health probe fails.

To watch the running instance from your own tooling, Self-host Telemetry covers exporting traces and metrics over OTLP to a collector you control.

Self-host Dots · This page in the graph

Connected Records13

Documented by
Mentions
References
13 connections.

Where To Go Next

Reading this as an agent?For Agents
Something wrong on this page?Dots On GitHub