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>
3.0 KiB
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
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:
- Cloudflare Zero Trust dashboard → Networks → Tunnels.
- Create a new tunnel (or rotate the token on the existing one).
- Paste the connector token into
.envasTUNNEL_TOKEN=.... - Restart:
docker compose up -d.
To map a new public hostname to a backend:
- Cloudflare dashboard → tunnel → Public Hostnames → Add a public hostname.
- Origin =
http://<container>:<port>(the backend must be on thehomelabDocker network so thiscloudflared-mcpcontainer can reach it by service name). - 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 aports:entry, you're misconfiguring — backends are reached outbound from the connector, not inbound. - External
homelabnetwork must exist before this stack starts. Backends must also be on thehomelabnetwork (or a network shared with it) forcloudflared-mcpto resolve them by container name. - One container, one tunnel. Multiple tunnels = multiple compose
stacks (or a parameterized invocation pattern — see the
roles/cloudflaredAnsible role inhomelab-ansiblefor that approach). :latestimage 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.