This sets up mantis to run in Docker on your own hardware and exposes it on the public internet at mantis.<your-domain> via Cloudflare Tunnel. Optionally, you can put parts of mantis behind Cloudflare Access for SSO (Google/GitHub/email-OTP/etc.). When this is the right choice:
  • You already own a domain that’s on Cloudflare DNS (or are willing to move one).
  • You want a mantis.<your-domain> URL rather than *.ts.net.
  • You like the idea of SSO-gating the dashboard for free (up to 50 users).
  • You’re OK with all mantis traffic flowing through Cloudflare’s edge.

Prerequisites

  • Docker + Docker Compose.
  • A domain you own. Any registrar.
  • Cloudflare account, free tier. Two things must be in place:
    1. The domain’s nameservers must point at Cloudflare. Add the domain in the main Cloudflare dashboard → it gives you two NS values → change them at your registrar → wait for propagation (5 min to a few hours).
    2. A Cloudflare Zero Trust team must be created (free, 50 seats). See Step 1 below.

Step 1 — Create a Zero Trust team

Cloudflare Zero Trust is a separate dashboard at https://one.dash.cloudflare.com (different URL from the main CF dashboard).
  1. Go to one.dash.cloudflare.com. First visit prompts for team name — becomes <team>.cloudflareaccess.com. Pick whatever.
  2. Choose the Free plan (50 seats).
  3. (Skip for now) Identity providers. We’ll come back to this only if you decide to enable Access in Step 7.

Step 2 — Create the tunnel

  1. Networks → Tunnels → Create a tunnel.
  2. Connector type: Cloudflared.
  3. Name: mantis → Save.
  4. The next screen shows install commands for various OSes. Ignore them — we run cloudflared inside Docker via our compose file. What you need is the key (long JWT) shown under “Install and run a connector”. Copy it.
  5. Click Next.
  6. Public Hostnames tab → Add a public hostname:
    • Subdomain: mantis (or your preference)
    • Domain: select your zone
    • Path: leave blank (matches everything)
    • Type: HTTP
    • URL: mantis:3000 ← this is the Docker service name; cloudflared resolves it via the compose network
    • Additional application settings → HTTP Settings → HTTP Host Header: mantis.<your-domain> (helps with cookie domain matching for the dashboard)
  7. Save → Cloudflare auto-creates the DNS record. No manual DNS needed.

Step 3 — Configure and start

./scripts/setup.sh generates POSTGRES_PASSWORD and MANTIS_API_KEY_PEPPER, which Compose refuses to start without — a plain cp .env.example .env leaves POSTGRES_PASSWORD empty and the stack won’t boot.

Step 4 — Verify

Browser: open https://mantis.<your-domain> → mantis /login.

Step 5 — Add public edge limits

Before sharing trigger URLs broadly, add Cloudflare rules for the public Mantis surfaces:
  • block oversized /c/*, /status/*, and /api/wallet/* URLs
  • rate-limit /c/* and /status/* by client IP
  • keep /inbox* and /api/inbox blocked unless this is a deliberate dev box
Use the exact expressions and suggested thresholds in edge-limits.md.

Step 6 — Bootstrap an API key

Paste into the dashboard login.

Step 7 (optional) — Cloudflare Access

This is the part where you need to make a decision. Before adding Access, decide what paths to gate. There are three reasonable levels:
The CLI now supports auth-passthrough for Cloudflare Access. The auth flow happens entirely on your machine: the CLI gets a short-lived JWT from cloudflared (via SSO) or sends a Service Auth header pair — and forwards it on every management API call. Mantis itself doesn’t validate Cloudflare JWTs; it just receives requests that have already passed CF’s gate, then validates the API key as usual. The server stays unaware of Cloudflare.
The CLI handles Cloudflare auth client-side. Server is none the wiser. Two flavors, pick one: Requires the cloudflared binary on every machine where you run the CLI.
Re-run mantis cloudflare login whenever the JWT expires (Cloudflare default: 24h).

Option 2 — Service Auth (headless / CI use, pre-shared key)

For machines where opening a browser isn’t an option (servers, CI runners, etc.):
The CLI sends CF-Access-Client-Id and CF-Access-Client-Secret headers on every /api/* request. Cloudflare Access validates them at the edge and passes through.

Cloudflare app configuration (either option)

  1. Settings → Authentication → Login methods. Add at least one (email OTP is simplest; SSO providers for teams).
  2. Access → Applications → Add an application → Self-hosted.
  3. Configuration:
    • Application name: “Mantis”
    • Session duration: 24h
    • Application Domain: mantis.<your-domain>
  4. Paths: gate dashboard and management API routes:
    • /
    • /login*
    • /logout*
    • /keys*
    • /settings*
    • /api/keys*
    • /api/hits*
    • /api/api-keys*
    • /api/device-profiles*
    • /api/audit*
    • /api/cron*
  5. Identity providers (for Option 1 / browser SSO): tick the methods you configured.
  6. Policies → add as needed:
    • Operator — Allow, Selector: Emails matching your address (for SSO / browser login).
    • Mantis CLI — Service Auth, Selector: your service key (only if using Option 2).
  7. Save.
What stays public (don’t gate these):
  • /c/* — mantis triggers. Must be public; this is the whole point.
  • /status/* — Uptime Kuma monitor URLs. Must be public so Uptime Kuma can poll.
  • /api/wallet/* — Apple Wallet callbacks. Required only if you enable .pkpass support.
  • /api/health — optional public health check. Gate it unless an external public monitor needs it.
  • /inbox* and /api/inbox — dev webhook inbox. Disable for production with ENABLE_DEV_INBOX=0 if you don’t need it.

After Access is on

Dashboard (browser):
  1. Visit https://mantis.<your-domain>/keys → Cloudflare redirects to <team>.cloudflareaccess.com → email-OTP / SSO login.
  2. After auth, redirects back to mantis.
  3. Mantis shows /login (paste API key).
  4. After paste, dashboard appears.
Effectively two-factor (CF SSO + mantis API key). If the double login bothers you, lobby for the “mint mantis session from CF JWT” feature — it would replace step 3. CLI:
  • With SSO mode: the CLI calls cloudflared access token --app=… for each request, getting a cached short-lived JWT. Transparent after the initial mantis cloudflare login.
  • With Service Auth: the CLI sends the stored Client-ID/Secret headers. No browser interaction ever.
  • All credential state lives on your machine. The mantis server never sees Cloudflare config and doesn’t need to verify anything Cloudflare-related — Access has already validated the request before mantis ever sees it.

Gotchas

  • Cloudflare terminates TLS. All mantis traffic — including potentially sensitive hit metadata — passes through Cloudflare’s edge in plaintext. If your mantis catches genuinely confidential content (email contents, document bodies), CF logs see it. Privacy tradeoff worth knowing.
  • Free Access seats: 50. Plenty for personal / small team. Past that, paid plan.
  • Origin protection. Cloudflare recommends locking your origin to only accept traffic from CF IPs. Mantis already binds to 127.0.0.1 (only reachable inside the Docker network, where cloudflared also lives), so this is taken care of without extra config.
  • Cloudflare Access cookie + Set-Cookie domain. If you see the dashboard login cookie not sticking, check the HTTP Host Header setting from Step 2.6 — it should be mantis.<your-domain>, not localhost:3000.

Uninstalling cleanly

  • In Cloudflare Zero Trust: Networks → Tunnels → mantis → Delete. Also removes the DNS record.
  • Access → Applications → Mantis dashboard → Delete if you set that up.
  • The DNS record auto-cleans on tunnel deletion (Cloudflare-managed).