Automated route and DNS synchronization between Docker containers and Cloudflare Tunnels
7.5K
⚡ Headless by Design:
hapheus/cf-tunnel-syncruns autonomously in the background without requiring any exposed ports or web server overhead.
📊 Need a GUI? Check out the optional companion containerhapheus/cf-tunnel-dashboard for visual monitoring and live route diagnostics.
hapheus/cf-tunnel-sync automatically discovers Docker containers across your host and synchronizes Cloudflare Zero Trust Tunnel ingress rules and proxied DNS (CNAME) records in real time.
http://, https://, tcp://), subpaths, and ports.MAX_DELETIONS_PER_SYNC=3).--preflight or PREFLIGHT_ONLY=true.--dry-run and GET /api/change-plan.💡 Quickstart: Looking for a fast walkthrough under 10 minutes? Check out the 10-Minute Canonical Quickstart Guide.
Deploy the core gateway stack alongside cloudflared:
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel run
environment:
- TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}
networks:
- tunnel_net
cf-tunnel-sync:
image: hapheus/cf-tunnel-sync:latest
container_name: cf-tunnel-sync
restart: unless-stopped
environment:
- CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
- CLOUDFLARE_ACCOUNT_ID=${CLOUDFLARE_ACCOUNT_ID:-}
- CLOUDFLARE_TUNNEL_NAME=${CLOUDFLARE_TUNNEL_NAME}
- DOMAIN_NAME=${DOMAIN_NAME}
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- tunnel_net
networks:
tunnel_net:
name: tunnel_net
Enable any container for automatic synchronization by applying Docker labels:
| Label | Description | Example |
|---|---|---|
cf.tunnel.enable | Enable tunnel synchronization (true or false). | true |
cf.tunnel.hostname | Fully qualified domain name for public ingress. | app.example.com |
cf.tunnel.port | Target port inside the container (auto-detected if single port exposed). | 8080 |
cf.tunnel.service | Target service URL / protocol override. | http://app:8080 or https://app:8443 |
cf.tunnel.path | Optional path prefix for path-based routing. | /api |
cf.tunnel.originRequest.noTLSVerify | Disable TLS certificate verification for upstream. | true |
cf.tunnel.originRequest.httpHostHeader | Custom HTTP Host header sent to upstream service. | backend.local |
cf.tunnel.originRequest.originServerName | SNI hostname for upstream TLS handshake. | backend.local |
cf.tunnel.access.aud | Cloudflare Access Application AUD tag for validation. | <AUD_KEY> |
| Variable | Required | Default | Description |
|---|---|---|---|
CLOUDFLARE_API_TOKEN | Yes | none | Cloudflare API Token with Account:Cloudflare Tunnel:Edit, Zone:DNS:Edit, and Zone:Zone:Read permissions. |
CLOUDFLARE_ACCOUNT_ID | No | auto-detected | Cloudflare Account ID (auto-detected from domain zone or token if omitted). |
CLOUDFLARE_TUNNEL_NAME | Yes* | none | Name of the Cloudflare Zero Trust Tunnel (*or specify CLOUDFLARE_TUNNEL_ID). |
CLOUDFLARE_TUNNEL_ID | No | auto-detected | Explicit Cloudflare Tunnel UUID. |
CLOUDFLARE_ZONE_ID | No | auto-detected | Explicit Zone ID for the root domain. |
DOMAIN_NAME | Yes | none | Root domain managed in Cloudflare (e.g. example.com). |
POLL_INTERVAL | No | 30 | Fallback polling interval in seconds for Docker state reconciliation. |
SYNC_ON_STARTUP | No | true | Reconcile all existing running containers immediately on startup. |
LOG_LEVEL | No | INFO | Logging level (DEBUG, INFO, WARNING, ERROR). |
DRY_RUN | No | false | When true, plans and diffs changes without mutating Cloudflare. |
PREFLIGHT_ONLY | No | false | When true, executes read-only readiness checks and exits immediately without mutations. |
MAX_DELETIONS_PER_SYNC | No | 3 | Maximum ingress/DNS route deletions permitted per sync cycle before safety guard aborts. |
ALLOW_MASS_DELETIONS | No | false | Explicit safety override to bypass blast-radius mass-deletion guard. |
LOG_FORMAT | No | text | Log format: text or json for structured observability. |
CLOUDFLARE_MAX_RETRIES | No | 3 | Maximum retry attempts for transient Cloudflare API errors (429, 5xx). |
NOTIFICATION_WEBHOOK_URL | No | none | Webhook URL for alerting on sync failures or blast-radius aborts. |
NOTIFICATION_WEBHOOK_TYPE | No | auto | Webhook format (auto, slack, discord, generic). |
NOTIFICATION_COOLDOWN_SECONDS | No | 300 | Cooldown window in seconds suppressing duplicate alerts. |
DOCKER_HOST | No | unix:///var/run/docker.sock | Docker daemon socket URI. |
STATUS_PAGE_ENABLED | No | true | Enable internal API & status endpoint on port 8090. |
STATUS_PAGE_PORT | No | 8090 | Internal status API listening port. |
METRICS_ENABLED | No | true | Expose Prometheus metrics at /metrics. |
cf-tunnel-sync performs all synchronization, self-healing, and preflight checks autonomously without needing a GUI.
If you would like a visual status dashboard, add the companion hapheus/cf-tunnel-dashboard container to your stack:
cf-tunnel-dashboard:
image: hapheus/cf-tunnel-dashboard:latest
container_name: cf-tunnel-dashboard
restart: unless-stopped
ports:
- "8090:80"
environment:
- DASHBOARD_AUTH_ENABLED=false # Set to true to require basic auth
networks:
- tunnel_net
Alternatively, you can publish port 8090:8090 directly on cf-tunnel-sync to access its internal status API and Prometheus metrics.
hapheus/cf-tunnel-dashboardContent type
Image
Digest
sha256:3ba2ba033…
Size
25.7 MB
Last updated
about 8 hours ago
docker pull hapheus/cf-tunnel-sync