Cutout
SELF-HOST GUIDE

Deploy your own Cutout

Cutout is self-host first. Click the Deploy to Cloudflare button (or fork manually and connect Cloudflare Builds), wire up bindings and secrets in the Cloudflare dashboard, and pushes to main deploy to your own Worker.

01 Deploy to Cloudflare

The fastest path is the one-click Deploy button on the repo README. It forks ananthb/cutout into your account, creates a Worker, auto-provisions the bindings declared in wrangler.toml (D1, KV, R2, Queues, Email send-binding, Analytics Engine), and connects the fork to Cloudflare Builds so future pushes deploy automatically.

If you'd rather wire it up by hand: fork ananthb/cutout, then in the Cloudflare dashboard create a Worker named cutout and connect the fork under Workers › cutout › Settings › Builds. Build configuration:

02 Bind resources in the dashboard

If you used the Deploy button, most bindings are already created. Otherwise create them and attach by name to the Worker under Workers › cutout › Settings › Bindings. The names must match the binding values in wrangler.toml:

03 Set runtime variables and secrets

Under Workers › cutout › Settings › Variables and Secrets, add:

The full reference is at the bottom of wrangler.toml.

04 Push to main

Cloudflare Builds runs npm run deploy: it installs worker-build, applies any pending D1 migrations, then runs wrangler deploy. After it succeeds, your Worker is live at cutout.<your-workers-subdomain>.workers.dev (or a custom route you've added under Settings › Domains & Routes).

05 Enable Email Routing on each domain

For every domain you want Cutout to handle, in the Cloudflare dashboard:

  1. Open Email › Email Routing and click Enable.
  2. Under Routes, edit the catch-all address, set the action to Send to a Worker, and pick the cutout worker.

06 Onboard each domain to Email Service

This is what lets Cutout send outbound mail (replies, proxy-mode forwards, and fan-out beyond the first destination).

  1. Dashboard › Email › Email Sending › Onboard Domain.
  2. Accept the DNS records (MX / SPF / DKIM / DMARC) on the cf-bounce.<yourdomain> subdomain.

07 Verify destination addresses

Cloudflare requires every email destination to be a verified address.

  1. Dashboard › Email › Email Routing › Destination addresses.
  2. Click Add destination address and enter the address (e.g. your real Gmail).
  3. Click the confirmation link sent to that address.

08 Protect /manage with Cloudflare Access

The management UI must not be public. Create an Access application whose policy covers cutout.<yourdomain>/manage/* (or whichever route you mapped to the worker). Copy the application's AUD tag and your team domain into the CF_ACCESS_AUD and CF_ACCESS_TEAM Worker variables (step 3) — Cloudflare Builds preserves them across redeploys.

09 Optional: enable Telegram and Discord destinations

Skip this step if you only want to forward to email. The /manage editor only offers a chat channel as a destination kind once the relevant bot secrets are set on the Worker. Add them under Workers › cutout › Settings › Variables and Secrets › Secrets:

Telegram

  1. Talk to @BotFather on Telegram and create a bot. Copy the HTTP API token.
  2. Add it as the secret TELEGRAM_BOT_TOKEN in the dashboard.
  3. Optional but recommended. Add the secret TELEGRAM_WEBHOOK_SECRET (any random string) so only Telegram can post to your worker.
  4. Register the webhook with Telegram (replace <TOKEN> and <HOST>; the secret_token param is only needed if you set TELEGRAM_WEBHOOK_SECRET):
    curl -X POST "https://api.telegram.org/bot<TOKEN>/setWebhook" \
      -d "url=https://<HOST>/telegram/webhook" \
      -d "secret_token=<WEBHOOK_SECRET>"
  5. Add the bot to each chat or channel you want messages forwarded to. To find a chat's chat_id, post anything in the chat then visit https://api.telegram.org/bot<TOKEN>/getUpdates: the integer (negative for groups, positive for DMs) is what you put in a rule destination as telegram:<chat_id>.

Discord

  1. Open the Discord Developer Portal and create an application. Under Bot, reset and copy the token. Under General Information, copy the Application ID and Public Key.
  2. Add all three as Worker secrets in the dashboard: DISCORD_BOT_TOKEN, DISCORD_APP_ID, DISCORD_PUBLIC_KEY.
  3. Back in the developer portal, set the Interactions Endpoint URL (under General Information) to https://<your-cutout-host>/discord/interactions. Discord pings this URL with a verification request: with the public key in place, the worker responds correctly and the URL saves.
  4. Invite the bot to your server via OAuth2 (scope bot, permission Send Messages for the target channels).
  5. For each Discord channel you want forwarded messages in, copy its Channel ID (Discord Settings › Advanced › Developer Mode, then right-click channel › Copy Channel ID) and use it in a rule destination as discord:<channel_id>.

Telegram replies (Telegram's native reply) and Discord replies (the Reply button on each forwarded message) route back through Cutout to the original email sender, so the chat client becomes a fully functional mailbox for that thread.

10 Add routing rules

Visit https://<your-cutout-host>/manage. Add Forward rules using glob patterns on the local part and domain. For email destinations, use the Proxy via rewrite mode toggle to ensure Reply-To works when replying via your custom domain (see How it works for the tradeoffs).

Updating

Pull from upstream into your fork's main branch (or merge a PR): Cloudflare Builds re-runs on every push to main.