# 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://:` (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://: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.