8.0 KiB
type, title, created, tags
| type | title | created | tags | ||||
|---|---|---|---|---|---|---|---|
| methodology | Cloudflare Infrastructure Setup | 2026-05-11 |
|
Cloudflare Infrastructure Setup
Architecture Overview
Browser ──► Cloudflare (orange cloud) ──► Tunnel ──► cloudflared ──► Traefik:8081
│
(tunnel entrypoint)
│
┌──────────┴──────────┐
secureweb tunnel
(direct internet) (via tunnel)
:443 :8081
LetsEncrypt plain HTTP
All external traffic goes through Cloudflare Tunnel. Traefik serves as the internal reverse proxy, routing by Host header. Traefik's LetsEncrypt certs exist for LAN/direct access but aren't used externally (Cloudflare edge terminates TLS for visitors).
Active Stack
| Component | Location | Notes |
|---|---|---|
| Tunnel name | home |
Created at Cloudflare Zero Trust → Networks → Tunnels |
| Tunnel ID | c29295c5-946a-4ddf-bdfe-7eafcd74faa3 |
Visible in dashboard |
| Token | .env TUNNEL_TOKEN=... |
Used by cloudflared to authenticate |
| cloudflared | production-1 Docker container | Runs with --restart unless-stopped |
| Traefik | production-1 Docker container | Ports 80, 443, 8080, 8082 |
| Traefik entrypoints | web (:80→redirect), secureweb (:443, TLS), tunnel (:8081), metrics (:8082) |
|
| Traefik config | /docker/appdata/traefik/traefik.yaml |
|
| Compose file | /docker/compose/docker-compose.yaml |
cloudflared service defined but runs standalone due to interpolation issues |
| Traefik labels | Per-service in docker-compose.yaml | Pattern: entrypoints=tunnel, Host(subdomain.gharbeia.net) |
1. Adding a Second Domain
Example: adding example.com alongside gharbeia.net.
Step 1: Cloudflare DNS
- Go to Cloudflare Dashboard → Add site → enter
example.com - Cloudflare scans existing DNS records — verify and continue
- Change nameservers at your registrar to Cloudflare's
- Wait for DNS to propagate
Step 2: Cloudflare Tunnel — add hostname
- Zero Trust → Networks → Connectors → Cloudflare Tunnels
- Select tunnel home → Edit
- Click Add a public hostname
- Set:
- Subdomain:
* - Domain:
example.com - Service Type: HTTP
- URL:
traefik:8081
- Subdomain:
- Save
Cloudflare DNS will automatically create a wildcard CNAME for *.example.com pointing to c29295c5-946a-4ddf-bdfe-7eafcd74faa3.cfargotunnel.com.
Step 3: Traefik — update cert resolver
Cloudflare wildcard certs only cover *.gharbeia.net. For *.example.com, either:
Option A: Add to Traefik's ACME config
In /docker/appdata/traefik/traefik.yaml, in certificatesResolvers.letsencrypt.acme:
dnsChallenge:
provider: cloudflare
resolvers:
- 1.1.1.1:53
- 1.0.0.1:53
propagation:
delayBeforeChecks: 120s # increase for wildcards
Then in entryPoints.secureweb.http.tls.domains:
domains:
- main: gharbeia.net
sans:
- "*.gharbeia.net"
- main: example.com
sans:
- "*.example.com"
Important: Before ACME DNS challenge can succeed for wildcard domains proxied via Cloudflare, you MUST create a DNS-only placeholder record for _acme-challenge to break the wildcard:
Go to Cloudflare DNS → Add record:
- Type: A
- Name:
_acme-challenge - Content:
127.0.0.1 - Proxy: DNS-only (gray cloud)
Without this, the proxied wildcard CNAME intercepts _acme-challenge.* queries and LetsEncrypt can't verify the TXT challenge records.
Option B: Skip HTTPS cert for now
If the second domain doesn't need Traefik's own cert (i.e., Cloudflare edge certs are sufficient), no Traefik changes needed. The tunnel handles everything.
Step 4: Add Traefik labels
For each service on the new domain, add Traefik labels in docker-compose.yaml:
labels:
- traefik.enable=true
- traefik.http.routers.servicename.rule=Host(`subdomain.example.com`)
- traefik.http.routers.servicename.entrypoints=tunnel
- traefik.http.services.servicename.loadbalancer.server.port=3000
Then restart the container to pick up labels (compose or docker run depending on env state).
2. Security Panic — Changing All Tokens and Settings
Step 1: Cloudflare API Token
- Go to Cloudflare Dashboard → My Profile → API Tokens
- Delete the old token
- Create Token → Edit zone DNS template:
- Permissions: Zone → DNS → Edit
- Zone Resources: Include → Specific zone →
gharbeia.net - TTL: No expiration (or set a reasonable one)
- Copy the new token
Step 2: Update .env on production-1
ssh production-1
# Edit the token
sed -i "s|^CLOUDFLARE_DNS_API_TOKEN=.*|CLOUDFLARE_DNS_API_TOKEN=<new-token>|" /docker/compose/.env
Step 3: Restart Traefik
Traefik runs standalone (not under compose). Restart it with the new token:
docker rm -f traefik
docker run -d --name traefik --restart unless-stopped --network networking \
-p 80:80 -p 443:443 -p 8080:8080 -p 8082:8082 \
-e CF_DNS_API_TOKEN="<new-token>" \
-e TZ="America/New_York" \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /docker/appdata/logs/traefik:/var/log \
-v /docker/appdata/traefik:/etc/traefik \
-v /docker/appdata/traefik/letsencrypt:/letsencrypt \
traefik:latest
Traefik will automatically renew its LetsEncrypt cert with the new token.
Step 4: Cloudflare Tunnel Token
If you also rotate the tunnel token:
- Go to Zero Trust → Networks → Tunnels → home
- Click the three dots → Recreate token
- Copy the new token
On production-1:
docker rm -f cloudflared
docker run -d --name cloudflared --restart unless-stopped --network networking \
-v /docker/appdata/cloudflared:/home/nonroot/.cloudflared \
cloudflare/cloudflared:latest tunnel --no-autoupdate run --token "<new-token>"
Also update the .env file:
TUNNEL_TOKEN=<new-token>
Step 5: Verify everything works
# Check cloudflared is connected
docker logs cloudflared --tail 5 | grep "Registered tunnel connection"
# Check Traefik is running and has routes
docker logs traefik --tail 10 | grep "Register"
# Test end-to-end
curl -sI https://git.gharbeia.net/ | head -5
# Should return HTTP/2 200
Step 6: Rotate other secrets (if needed)
Other credentials in .env that may need rotation:
AUTHENTIK_SECRET_KEY— generate new:openssl rand -base64 60 | tr -d '\n'POSTGRESQL_PASSWORD— generate newEMAIL_PASSWORD— Fastmail app passwordGITEA_TOKEN— Gitea settings → ApplicationsOPENROUTER_API_KEY— OpenRouter dashboard
Troubleshooting
Cert renewal fails with "Invalid format for Authorization header"
The Cloudflare API token in CF_DNS_API_TOKEN is wrong or expired. Generate a new one and restart Traefik.
Cert renewal stuck on "Waiting for DNS record propagation"
Likely the proxied wildcard CNAME is intercepting _acme-challenge subdomain. Add a DNS-only A record for _acme-challenge → 127.0.0.1 in Cloudflare DNS to break the wildcard.
Tunnel shows 502 Bad Gateway
- Check cloudflared logs:
docker logs cloudflared --tail 10 - If
connection refused→ Traefik isn't listening on the expected port - If
tls: unrecognized name→ SNI mismatch (revert to HTTP mode in tunnel config)
Adding a new service
- Add Traefik labels to the service in docker-compose.yaml
- If the container was started without labels, recreate it with
docker runincluding-lflags (compose may fail due to .env interpolation issues) - The tunnel wildcard already catches
*.gharbeia.net— no tunnel changes needed