Self-hosting

The full Doop on your own server — one Docker container plus Postgres.

Doop is open source under AGPL-3.0. The canvas, the MCP server agents connect to, the Doop Agent, imports, exports, prototypes and integrations all ship in one image. On your own instance billing is off, so every workspace is unlimited.

This page covers the Community edition, which is free. If you need a commercial license, SAML and SCIM, or a support SLA, see Enterprise.

Quick start with Docker Compose

The repository's docker-compose.yml builds the app and starts it next to a Postgres 16 container. You need Docker with Compose. The image includes Chromium, which renders frame screenshots and exports.

$ git clone https://github.com/kgoedecke/doop && cd doop
$ BETTER_AUTH_SECRET=$(openssl rand -hex 32) docker compose up -d

Open http://localhost:4400 and create an account. Doop runs database migrations on every boot, so there is no setup step. The first account is a regular user; see admins to give yourself the admin role.

Keep the secret. BETTER_AUTH_SECRET signs sessions and agent tokens, so if you change it, everyone is signed out and every connected agent has to authenticate again. Put it in a .env file next to docker-compose.yml instead of typing it each time:

# .env — read by docker compose
BETTER_AUTH_SECRET=paste-a-long-random-string
BETTER_AUTH_URL=https://doop.example.com

Keep uploads across restarts

Postgres data lives in the doop-pg volume. Uploaded images, fonts and imported assets go to /app/data/assets inside the app container unless you configure object storage. The stock compose file does not mount that path, so add a volume before you rely on it. Without one, uploads are lost when the container is rebuilt:

services:
  doop:
    volumes:
      - doop-data:/app/data
volumes:
  doop-data:

Deploy to production

Any host that runs a container works. Railway, Fly.io, Render, Dokploy and Coolify all detect the Dockerfile on their own, and a plain VM running Docker Compose works too. For a deployment people depend on:

To check the exact production image locally before you deploy it:

$ docker build -t doop .
$ docker run -p 4400:4400 -e BETTER_AUTH_URL=http://localhost:4400 -e BETTER_AUTH_SECRET=dev-only doop

Without Docker

Doop installs and builds with Bun and the server runs on Node, so you need both on your PATH. A production build serves the app, API, WebSocket and MCP endpoint from one process on :4400:

$ bun install
$ bun run build && bun run start

Frame screenshots and exports need Chrome or Chromium on the machine. It is detected automatically, or you can set CHROME_PATH.

Local development

bun run dev starts the web app on http://localhost:4300 and the API, WebSocket and MCP server on :4400, with an embedded Postgres in data/pg. It needs no configuration at all.

Configuration

Configuration is entirely environment variables. Each one is validated once at boot, so a misconfigured instance refuses to start rather than half-working. Every variable is optional except the two marked required, and each optional feature turns off quietly until its variables are set. This is the short list most self-hosters touch; .env.example documents the rest.

WhatVariablesWhen unset
Core BETTER_AUTH_SECRET, BETTER_AUTH_URL (both required in production), DATABASE_URL, TRUSTED_ORIGINS, PORT Embedded Postgres in data/pg, localhost origins
Email SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_SECURE, EMAIL_FROM Emails print to the server log; no verification
Sign-in OIDC_*, GOOGLE_*, MICROSOFT_*, APPLE_*, SIGNUP_EMAIL_DOMAINS, ADMIN_EMAILS Email and password only, open signup
Object storage BUCKET, ACCESS_KEY_ID, SECRET_ACCESS_KEY, ENDPOINT, REGION Uploads stored on disk under data/assets
Doop Agent ANTHROPIC_API_KEY, RESIDENT_TASK_LIMIT, DOOP_AGENT_*, AZURE_OPENAI_* Runs only on model accounts people connect
Image generation OPENAI_API_KEY, GEMINI_API_KEY, ARK_API_KEY, DOOP_IMAGE_MODEL Runs only on accounts people connect
Agent search PEXELS_API_KEY (photos), BRANDFETCH_CLIENT_ID (logos), CONTEXT_DEV_API_KEY No photo search; open-source and favicon logos
Integrations SLACK_*, LINEAR_*, META_APP_*, GITHUB_APP_* That integration is hidden; GitHub import uses a personal token
Billing STRIPE_SECRET_KEY, STRIPE_PRICE_TEAM_*, STRIPE_WEBHOOK_SECRET Billing off, every workspace active
Webhooks WEBHOOK_ALLOW_PRIVATE_URLS Webhook URLs on private networks are refused

Object storage

Set BUCKET, ACCESS_KEY_ID, SECRET_ACCESS_KEY, ENDPOINT and REGION to store uploads in any S3-compatible store: AWS S3, Cloudflare R2, MinIO, Backblaze B2 or Railway buckets. Use this if you run more than one app instance or don't want to manage a data volume.

Accounts and sign-in

People sign in with an email and password by default, and signup is open. Canvases are private to their owner until shared, just like on doop.design. Agents connected over MCP act as the account that approved them and get exactly that person's access.

Email

Set the SMTP_* variables and EMAIL_FROM. Signups then need email verification, and password resets send real links. Without SMTP, every email, links included, is printed to the server log, so the flows still work while you test. Set REQUIRE_EMAIL_VERIFICATION=false if you want people to get in before they verify.

Restrict signup

SIGNUP_EMAIL_DOMAINS=example.com limits new accounts to one email domain. Use a comma-separated list for several domains. It applies to every sign-in method, and existing accounts are unaffected.

SSO and social sign-in

These sit alongside email and password; they don't replace it. Each one needs its variables set together:

A partial set refuses to boot. If an SSO sign-in's email matches an existing account and the provider marks that email verified, it signs in to that account.

Admins

ADMIN_EMAILS (comma-separated) names the accounts that get the admin role. You can name someone before or after they sign up. Admins get /admin, which lists every canvas and account on the instance and offers a read-only “view as” session for support.

An address is promoted only once it is verified, so in production this needs SMTP. Otherwise anyone could sign up with your address and take the role. An instance without SMTP promotes nobody and warns at boot; in that case, set the role in the database yourself.

The Doop Agent on your server

The built-in Doop Agent works on a self-hosted instance with no server key. Each person connects their own model account in Settings, and their runs bill to it. To let the server pay for some runs instead, give it a key and raise the free-task limit (it is 0 by default):

ANTHROPIC_API_KEY=sk-ant-...
RESIDENT_TASK_LIMIT=20

If your organisation's credits or compliance rules are on Azure OpenAI, the server-paid runs can use it instead:

DOOP_AGENT_PROVIDER=azure
AZURE_OPENAI_ENDPOINT=https://my-resource.openai.azure.com
AZURE_OPENAI_API_KEY=...
AZURE_OPENAI_DEPLOYMENT=my-deployment

People can also connect a custom provider. By default its URL must be a public HTTPS address. For models on your own network (Ollama, vLLM, LM Studio), set DOOP_CUSTOM_PROVIDER_PRIVATE_HOSTS=true to allow private and loopback hosts and plain HTTP. Don't do this on an instance strangers can sign up to. Set DOOP_CUSTOM_PROVIDERS=false to turn the feature off.

Agents you bring yourself over MCP (Claude Code, Codex, Cursor, ChatGPT) need no key on the server, because they run on their own subscriptions.

Connect agents to your instance

Every setup in Get started works the same against your server. Replace https://doop.design/mcp with your own origin:

$ claude mcp add --transport http doop https://doop.example.com/mcp

The MCP endpoint uses OAuth with dynamic client registration, so there is nothing to register by hand. The first tool call opens a browser where you sign in to your instance and approve the agent. If the approval page points at the wrong host, BETTER_AUTH_URL is wrong.

Integrations

On a self-hosted instance you register the third-party apps yourself, and each integration appears once its variables are set. The steps are in the Integrations and Automations docs: Slack and Linear apps, the Meta app for ad automations, and a GitHub App for GitHub import. Webhooks need no setup, but by default they refuse URLs on private networks. Set WEBHOOK_ALLOW_PRIVATE_URLS=true to send them to internal services.

Workspaces and billing

Billing is off unless you turn it on. Without STRIPE_SECRET_KEY, every workspace is active with unlimited members and canvases. You need Stripe only if you resell Doop to your own customers. In that case, the repository's scripts/stripe-setup.mjs creates the products, and the README covers the webhook.

Upgrades and backups

To upgrade, pull the latest code and rebuild. Migrations run automatically on boot, so there is no separate migrate step:

$ git pull
$ docker compose up -d --build

Changes are listed in the changelog. Back up two things: the Postgres database and your uploads (the /app/data volume or your bucket). With the compose setup, this dumps the database:

$ docker compose exec db pg_dump -U doop doop > doop-$(date +%F).sql

Troubleshooting

Stuck? Ask on Discord or open an issue on GitHub.

License

Doop is licensed under the GNU AGPL v3. You can run it, modify it and use it inside your company freely. If you offer a modified version to others over a network, you must publish your changes under the same license. The Doop name and logo are trademarks, so please rebrand a service you derive from it.

The Enterprise license removes the AGPL obligations and adds SAML and SCIM, audit logs, a support SLA, and help with deployment and upgrades.