---
title: Set up the Cloudflare Email gateway
description: Deploy a Cloudflare Worker that receives, stores and sends mail for one of your domains.
---

The Cloudflare Email gateway is a Cloudflare Worker. It receives mail through Email Routing, stores messages in D1 with raw mail and attachments in R2, and sends through Cloudflare Email Sending. You deploy one Worker per domain.

## Before you start

- The domain is a zone on your Cloudflare account. Email Routing needs Cloudflare DNS.
- Your Cloudflare account is on the **Workers Paid** plan, which Email Sending needs to send to any recipient.
- You have a Cloudflare API token with **Zone, Email Routing: Edit** and **Zone, DNS: Edit**, and you can deploy Workers with wrangler.
- You have [Bun](https://bun.sh) and a copy of the Metis Suite source code:

```bash
git clone https://github.com/trymetis/metis.git
cd metis
bun install
cd apps/gateway-cloudflare
```

## Deploy the Worker

1. **Create the database**

    ```bash
    bun run db:create
    ```

    It prints a D1 database id. Paste it into `wrangler.jsonc` as
    `database_id`, and set `MAIL_DOMAIN` there to your domain.

2. **Create the storage bucket**

    ```bash
    bun run r2:create
    ```

3. **Create the tables**

    ```bash
    bun run db:apply
    ```

4. **Set the admin token**

    Generate a long random value, for example with `openssl rand -hex 32`,
    then store it as the Worker's admin token:

    ```bash
    bun run secret
    ```

    Keep this value: you need it to create mailbox tokens.

5. **Deploy**

    ```bash
    bun run deploy
    ```

    It prints the Worker's `workers.dev` address. You need it below.

6. **Connect your domain**

    Run the setup with your API token, your zone id and the addresses you
    want to receive mail for:

    ```bash
    CLOUDFLARE_API_TOKEN=... CLOUDFLARE_ZONE_ID=... \
      MAIL_ADDRESSES=alice@example.com,bob@example.com \
      bun run setup
    ```

    The setup turns on Email Routing for the zone, creates a routing rule for
    each address, sets the domain up for Email Sending, and adds the DNS
    records sending needs (SPF, DKIM, DMARC and a bounce record). It is safe
    to run again: run it with more addresses whenever you add one. Set
    `WORKER_NAME` if you named the Worker differently.

    Without `MAIL_ADDRESSES`, the setup sends every address on the domain to
    the Worker instead (a catch-all). Aliases then work with no setup, but
    the domain accepts more spam. Mail for an address with no token is kept,
    and it appears once you create a token for that address.

7. **Register it in Metis Suite**

    Open **Dashboard**, **System**, **Cloudflare Email** under **Providers**.
    Click **Add server** and fill in:

    - **Name**: what users see in the connect dialog.
    - **Domain**: your domain, such as `example.com`, without `https://`.
    - **Worker endpoint**: the Worker's `https://` address.

    Click **Save changes** and enter your authenticator code.

    You can also set the starting list before the first start:

    ```bash
    METIS_CLOUDFLARE_SERVERS=[{"name":"Example Mail","domain":"example.com","endpoint":"https://metis-cf-mail.you.workers.dev"}]
    ```

8. **Create mailbox tokens**

    Create a token for each address and give it to its owner. See
    [Mailbox tokens](/self-hosting/mail-gateways/mailbox-tokens).

## Cloudflare limits

- A D1 row holds at most 2 MB, so message bodies are cut at 1 MB. Raw mail and attachments are kept in R2 in full.
- Email Sending includes 3,000 emails a month on Workers Paid, then is billed per 1,000. A message can have at most 50 recipients. Email Sending is in beta.
- Email Routing allows at most 200 routing rules per zone. Beyond that, use catch-all mode.
