Skip to content

Publishing & Custom Domains

One-click publish: building, deploying, and going live on the web in seconds

You can ship a Doable project to the public web with one click.

Publish

In the editor toolbar, click Publish.

What happens:

  1. The API runs the project's production build (vite build).
  2. The output is copied to SITES_DIR/<project-slug>/.
  3. The reverse proxy (Caddy or nginx) starts serving the site at <slug>.<DOABLE_DOMAIN>.
  4. The project's row gets a published_at timestamp.

You'll see a green Live badge with the URL; click to open in a new tab.

Re-publish

Each click of Publish rebuilds and redeploys. Old versions aren't kept by default; for that, see Version History.

Unpublish

Project → Settings → Publishing → Unpublish. The site stops being served (returns 404) and published_at is cleared.

The default URL

Your project lives at:

https://<project-slug>.<DOABLE_DOMAIN>

DOABLE_DOMAIN is set by the operator (typically doable.me or a self-hosted equivalent). The slug is derived from the project name and editable in Project → Settings → URL slug.

Custom domains

Want myapp.com instead?

  1. Project → Settings → Domain → Add custom domain.
  2. Enter your domain.
  3. Doable shows the DNS record you need to set on your DNS provider:
  4. For most setups: a CNAME pointing to <DOABLE_DOMAIN>.
  5. For Cloudflare-Tunnel-based deployments: a CNAME to <CLOUDFLARE_TUNNEL_ID>.cfargotunnel.com (orange cloud).
  6. Add the record at your registrar / Cloudflare.
  7. Click Verify. Doable checks DNS, issues an SSL cert (Let's Encrypt or via Cloudflare), and starts serving the site at the custom domain.

The status will move through pending_dns → pending_verification → active. If it gets stuck:

  • Wait; DNS propagation can take up to 48h.
  • Click Re-verify.
  • Check Project → Settings → Domain → Diagnostics for the exact error.

Multiple domains

A project can have multiple custom domains (e.g. myapp.com and www.myapp.com). Add them one at a time.

The free plan limit on custom domains is set by the operator (packages/shared/src/constants.ts).

Apex domains

Most DNS providers don't allow CNAME at the apex (example.com vs www.example.com). Use one of:

  • ALIAS / ANAME / CNAME-flattening records (Cloudflare, DNSimple, Route 53 Alias).
  • An A record to your server's static IP.

SSL

You don't manage certs yourself. Caddy (or nginx + certbot) does it on demand. Renewals happen automatically.

Sites' env vars

Your project can have published-time env vars under Project → Settings → Environment variables. These are baked into the static build (VITE_* prefixed for Vite to expose them to the client).

For server-side env vars (API routes), Doable's published projects are static-only today; you'd need to deploy a separate backend.

Security & isolation

Each published project is served from its own subdirectory in SITES_DIR. The reverse proxy enforces the URL → directory mapping; users can't traverse out of their site root.

The build runs inside the same dovault jail as the dev server, so a malicious or buggy build script can't affect other users' files.

See also


MCP Tools & Integrations in Published Apps

If your project uses MCP tools (e.g. connected databases, HPCA, Supabase) or integrations (Slack, Stripe, etc.), they continue to work after publishing, automatically.

What happens on publish

  1. Doable scans your source for doable.mcp.call(...) and doable.integrations.run(...) patterns
  2. A scoped API key is created, only authorized for the tools your app uses
  3. The key is baked into your production bundle at build time
  4. Your published site calls the Doable API proxy directly (cross-origin, secured by the key)

No configuration needed

The @doable/sdk detects whether it's running in:

  • Preview (in the editor) → uses a short-lived JWT token
  • Published site → reads the auto-provisioned API key from the build

Same code, zero changes between environments.

Adding or changing MCP tools after first publish

If you add a new MCP connector, remove one, or the AI generates code using different tools:

  1. The AI generates code using the new MCP tools
  2. Click Publish again: this is required
  3. The auto-provisioner re-scans your code, detects new/changed tools, and updates the key's scope
  4. The published site now has access to the updated tools

Always re-publish after changing MCP connectors

The API key scope is determined at build time by scanning your source code. If you add a new MCP to your workspace and the AI writes code using it, that code won't work on the live site until you re-publish.

You do NOT need to re-publish when the MCP server's underlying data changes (e.g. new database records), only when the set of tools your app calls changes.

See API Keys & Auto-Provisioning for the full list of what triggers a re-publish.

Security

Published apps never see raw MCP server credentials. The connector proxy:

  • Decrypts credentials server-side
  • Validates the API key's tool scope and origin
  • Rate-limits requests
  • Logs every call for audit

See API Keys & Auto-Provisioning for the full security model.