repo for storing the cloudflared-contents
Go to file
Travis Herbranson 20ab511fe1 chore: remove dead uv-init scaffolding (pyproject/uv.lock/.python-version)
These were the remaining `uv init` residue alongside the already-removed
main.py stub — not part of the build/deploy path. Also drops the now-stale
'leftover uv-init artifacts' note from README.md since the files are gone.
2026-05-22 20:37:53 -04:00
.gitignore init commit to create the project 2026-05-10 13:45:14 -04:00
compose.yml docs: fix dangling docker-compose.yml reference in compose.yml usage comment 2026-05-22 19:52:57 -04:00
README.md chore: remove dead uv-init scaffolding (pyproject/uv.lock/.python-version) 2026-05-22 20:37:53 -04:00

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:

  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.