This sets up mantis to run in Docker on your own hardware (laptop, home server, NAS, VPS) and expose it through Tailscale. There are two useful modes: The split mode is closest to the Cloudflare Access posture: public tripwires keep working, while management traffic only works from your tailnet.

Prerequisites

  • Docker + Docker Compose.
  • A Tailscale account.
  • A reusable Tailscale auth key. Split mode starts two Tailscale nodes, so a one-use key will register only the first one.

Step 1 — One-time tailnet configuration

These settings live in the Tailscale admin console and only need to be done once per tailnet.
  1. Enable HTTPS CertificatesDNSEnable HTTPS. This lets Tailscale issue certificates for *.<tailnet>.ts.net.
  2. Enable FunnelFunnelEnable Funnel for this tailnet. Without this, Funnel config is ignored and the public hostname is tailnet-only.
  3. If you use ACLs, add a tag owner so the Docker nodes can register tagged:

Step 2 — Generate an auth key

Go to Settings → Keys → Generate auth key with these properties: Copy the tskey-auth-... value.

Option A — Simple Funnel

This is the existing one-host setup. It exposes the whole app publicly through Funnel; dashboard and API routes still require mantis auth.
./scripts/setup.sh generates the two secrets Compose won’t start without — POSTGRES_PASSWORD and MANTIS_API_KEY_PEPPER — so you only fill in the Tailscale and URL values above. A plain cp .env.example .env leaves POSTGRES_PASSWORD empty and the stack refuses to boot. Verify:

Option B — Split Serve + Funnel

This is the recommended Tailscale posture when you want /api/* and the dashboard off the public internet. It starts two Tailscale sidecars:
  • mantis-private.<tailnet>.ts.net uses Tailscale Serve only. This is where you open the dashboard and point the CLI.
  • mantis-public.<tailnet>.ts.net uses Tailscale Funnel. Mantis allows only public routes on this host: /c/*, /status/*, and /api/wallet/*.
Run ./scripts/setup.sh first to create .env with the two required secrets (POSTGRES_PASSWORD and MANTIS_API_KEY_PEPPER), then add the split-host configuration to it:
Start it:
Verify public behavior from a non-tailnet network, for example a phone on cellular:
Expected:
  • /status/nonexistent reaches mantis and returns 404 not_monitored.
  • /login returns a plain 404.
  • /api/keys returns a plain 404, not a mantis auth challenge.
Verify private behavior from a device on your tailnet:
Use the private hostname for dashboard and CLI. Use the public hostname for generated mantis trigger/status URLs; PUBLIC_BASE_URL already handles that. mantis doctor verifies the private API, then checks the public hostname hides /login and /api/* while leaving /status/* reachable.

Public edge limits

Tailscale Funnel is not a full application-layer WAF. Split mode is the main protection: only /c/*, /status/*, and /api/wallet/* should be reachable on the public Funnel hostname. If the public hostname will be widely exposed or advertised, use one of these:
  • Prefer Cloudflare Tunnel instead of Funnel when you own a domain and want per-path URL and rate limiting rules.
  • Or put nginx/Caddy between Tailscale and Mantis, then point the Tailscale Serve/Funnel config at that proxy.
Suggested limits and a minimal nginx shape are in edge-limits.md.

Optional public routes

The public-only guard is deliberately tight. These are opt-in:

Gotchas

  • Funnel is public, Serve is private. Tailscale Serve can identify tailnet users and obeys tailnet ACLs; Funnel is for unauthenticated public internet exposure. The split setup uses both.
  • One port cannot be both private and public on one Tailscale node. That is why split mode uses two Tailscale containers and two hostnames.
  • Funnel hostnames are *.ts.net. Tailscale Funnel is tied to tailnet DNS names. If you need mantis.yourdomain.com plus browser SSO, Cloudflare Tunnel + Access is still the cleaner fit.
  • Apple Wallet callbacks live under /api/wallet/*. They stay public in split mode because installed passes need to call back without being on your tailnet.
  • The dev inbox is intentionally unsafe for production. It is an unauthenticated capture buffer. Keep ENABLE_DEV_INBOX=0 unless you are actively testing.

Uninstalling cleanly

Simple mode:
Split mode:
The ephemeral nodes disappear from Machines shortly after shutdown. Revoke the auth key in Settings → Keys if you do not plan to reuse it.