cloudflared/README.md
Travis Herbranson 2f4fc25543 docs: write operator README
Repo had a 0-byte README. New one covers:
- Single-service inventory with the load-bearing cloudflared-mcp
  container name (vault-mcp's legacy network aliases depend on it)
- Token-mode + dashboard-managed ingress contract
- .env contract (TUNNEL_TOKEN)
- Token rotation flow + how to map a new public hostname
- Cross-reference to homelab-ansible's roles/cloudflared README for
  the redirect-loop gotcha on Traefik backends
- No-ports / one-tunnel / :latest gotchas

Working tree has an in-flight docker-compose.yml → compose.yml rename
(D docker-compose.yml + ?? compose.yml). Intentionally not part of
this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 07:50:25 -04:00

75 lines
3.0 KiB
Markdown

# cloudflared
Token-mode Cloudflare tunnel that fronts the homelab's MCP services and
admin UIs. Outbound-only from the host — no inbound ports.
Routes `*.herbylab.dev` hostnames (e.g. `vault.herbylab.dev`,
`auth.herbylab.dev`, `trellis.herbylab.dev`) to in-network services on
the shared `homelab` Docker network. **Ingress rules live in the
Cloudflare Zero Trust dashboard**, not in this repo.
## Service
| Service | Image | Container name |
|---------------|----------------------------------|---------------------|
| `cloudflared` | `cloudflare/cloudflared:latest` | `cloudflared-mcp` |
The `cloudflared-mcp` container name is **load-bearing**: it's the same
hostname the legacy network aliases on `vault-mcp` (`homelab-mcp`,
`herbydev-mcp`) expect, so renaming the container breaks dashboard
ingress until the tunnel config is repointed.
## Run it
```bash
docker compose up -d
docker compose logs -f cloudflared
```
## Configuration
| File | Purpose |
|----------------|---------------------------------------------------------|
| `compose.yml` | Single-service definition + external `homelab` network |
| `.env` | `TUNNEL_TOKEN` (from Cloudflare Zero Trust dashboard) — gitignored |
To create / rotate the tunnel:
1. Cloudflare Zero Trust dashboard → Networks → Tunnels.
2. Create a new tunnel (or rotate the token on the existing one).
3. Paste the connector token into `.env` as `TUNNEL_TOKEN=...`.
4. Restart: `docker compose up -d`.
To map a new public hostname to a backend:
1. Cloudflare dashboard → tunnel → Public Hostnames → Add a public
hostname.
2. Origin = `http://<container>:<port>` (the backend must be on the
`homelab` Docker network so this `cloudflared-mcp` container can
reach it by service name).
3. No restart needed — the connector reloads dashboard config on its own.
For HTTPS-only backends (Traefik et al.), use
`https://<container>:443` with "No TLS Verify" — see the
`roles/cloudflared` README in `homelab-ansible` for the redirect-loop
gotcha when targeting `:80`.
## Gotchas
- **Token-mode = no `ports:` block.** This is outbound-only by
construction. If you find yourself adding a `ports:` entry, you're
misconfiguring — backends are reached *outbound* from the connector,
not inbound.
- **External `homelab` network must exist** before this stack starts.
Backends must also be on the `homelab` network (or a network shared
with it) for `cloudflared-mcp` to resolve them by container name.
- **One container, one tunnel.** Multiple tunnels = multiple compose
stacks (or a parameterized invocation pattern — see the
`roles/cloudflared` Ansible role in `homelab-ansible` for that
approach).
- **`:latest` image tag.** No pinning. If a connector upgrade breaks
ingress, pin to the last-known-good tag and re-deploy.
- The repo also contains some leftover uv-init artifacts (`main.py`,
`pyproject.toml`, `uv.lock`, `.python-version`). These are not part
of the deployment.