Skip to content

feat: add Cloudflare Tunnel integration#923

Open
lbajsarowicz wants to merge 11 commits into
wardenenv:mainfrom
lbajsarowicz:feature/cloudflared-integration
Open

feat: add Cloudflare Tunnel integration#923
lbajsarowicz wants to merge 11 commits into
wardenenv:mainfrom
lbajsarowicz:feature/cloudflared-integration

Conversation

@lbajsarowicz

@lbajsarowicz lbajsarowicz commented Mar 22, 2026

Copy link
Copy Markdown
Contributor

Summary

  • New warden cf command for tunnel lifecycle management (login, create, delete, status, update, logout)
  • Conditional cloudflared global service (enabled by WARDEN_CLOUDFLARED_TUNNEL_ID in ~/.warden/.env)
  • Dynamic config generation scanning dev.warden.cf.domain container labels
  • New nginx-public.base.yml partial for TRAEFIK_PUBLIC_DOMAIN routing
  • Config auto-regenerates on env up/down/stop/start
  • CHANGELOG entry added

Related PRs

All PRs must be merged together.

Test plan

  • Set up: warden cf loginwarden cf createwarden svc up
  • Configure project with TRAEFIK_PUBLIC_DOMAIN in .env and warden env up
  • Verify warden cf status shows tunnel and connected domains
  • Verify ~/.warden/etc/cloudflared/config.yml has correct ingress rules
  • Verify warden cf update regenerates config
  • Verify warden env down removes domain from config
  • Verify warden cf delete and warden cf logout clean up correctly
  • Verify no-op when WARDEN_CLOUDFLARED_TUNNEL_ID is not set

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Cloudflare Tunnel integration to Warden, including a new warden cf command, conditional global cloudflared service startup, and automatic tunnel config regeneration based on running environments that opt in via TRAEFIK_PUBLIC_DOMAIN.

Changes:

  • Introduces warden cf subcommands to manage Cloudflare Tunnel lifecycle and local credential/config files.
  • Adds conditional cloudflared global service and generates ~/.warden/etc/cloudflared/config.yml by scanning container labels.
  • Adds an nginx-public Traefik routing partial to opt environments into public-domain routing.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
utils/core.sh Adds regenerateCloudflaredConfig to generate cloudflared config from running container labels and restart cloudflared
commands/svc.cmd Conditionally includes the cloudflared compose file and triggers config regeneration on svc up
commands/env.cmd Adds nginx-public partial opt-in and triggers cloudflared config regeneration on env lifecycle actions
docker/docker-compose.cloudflared.yml Defines the global cloudflared service container and mounts generated config/credentials
environments/includes/nginx-public.base.yml Adds Traefik router labels for TRAEFIK_PUBLIC_DOMAIN and emits dev.warden.cf.domain label
commands/cf.cmd Implements warden cf (login/create/delete/status/update/logout) behaviors
commands/cf.help Adds help/usage text for the new cf command
CHANGELOG.md Documents the new Cloudflare Tunnel integration under UNRELEASED

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread commands/cf.cmd
Comment thread commands/svc.cmd
Comment thread utils/core.sh Outdated
Comment thread utils/core.sh
Prevents trailing \r on Windows/WSL from breaking tunnel operations.
- Remove duplicate regenerateCloudflaredConfig calls in pre-down/pre-up blocks
  (keep only the post-command call when containers are in final state)
- Add preflight warning in svc.cmd when config.yml is missing
- Add varnish-public.base.yml for TRAEFIK_PUBLIC_DOMAIN with varnish enabled
  (varnish.base.yml disables traefik on nginx, so labels must go on varnish)
- Fix comment on docker restart to reflect actual behavior
@bap14

bap14 commented Mar 23, 2026

Copy link
Copy Markdown
Member

Interesting. I'll have to give this a test drive before approving, but seems like an almost no-brainer alternative to ngrok.com.

@navarr

navarr commented Mar 23, 2026

Copy link
Copy Markdown
Member

I think my biggest surprise is that (at least according to Google's AI, because I didn't do more research than that) that Cloudflare offers this free?

I've already merged in the image because there's no potential harm from building it; but the rest I want to spend more time looking over

@lbajsarowicz

Copy link
Copy Markdown
Contributor Author

@navarr I've been using that for more than a year for my Home Assistant and my microservices (e.g. MCPs, self-hosted LMStudio). Finally I found motivation to introduce that to Warden :)

@mekedron

mekedron commented Jun 11, 2026

Copy link
Copy Markdown

@navarr indeed, Cloudflare offers it for free, but it only requires a domain name. I have been using it for years.

However, I would strongly recommend that anyone who needs to test a local website on other devices, rather than on a host, consider Tailscale. It is free, requires no configuration, and does not require a domain name. In addition, with Tailscale, it is possible to use Funnel and expose certain ports and URLs to the internet, making it possible to test webhooks and other functionality. I highly recommend it. They have a free plan for 6 users and unlimited number of devices.

With Cloudflare you would need to play around with Zero trust policies, firewall, configuration, DNS and so on.

If someone needs, they can easily inject the Cloudflare tunnel container using warden-env.yaml like this:

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: "${WARDEN_ENV_NAME}-cloudflared"
    command: tunnel --no-autoupdate run
    environment:
      - TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN}
    networks:
      - default
    restart: unless-stopped

  varnish:
    labels:
      # Only these paths on ${TRAEFIK_TUNNEL_BACKEND_SUBDOMAIN}.${TRAEFIK_TUNNEL_DOMAIN} go to varnish (high priority)
      - "traefik.http.routers.${WARDEN_ENV_NAME}-varnish.priority=100"
      - "traefik.http.routers.${WARDEN_ENV_NAME}-varnish.rule=Host(`${TRAEFIK_TUNNEL_BACKEND_SUBDOMAIN}.${TRAEFIK_TUNNEL_DOMAIN}`) && (Path(`/${MAGENTO_ADMIN_FRONTNAME}`) || PathPrefix(`/${MAGENTO_ADMIN_FRONTNAME}/`) || PathPrefix(`/media/`) || PathPrefix(`/static/`) || PathPrefix(`/rest/`) || Path(`/graphql`) || Path(`/stripe/webhooks`) || Path(`/stripe/webhooks/`) || Path(`/robots.txt`))"
      - "traefik.http.routers.${WARDEN_ENV_NAME}-varnish.tls=true"

  nginx:
    labels:
      # Backend host(s) only; explicitly exclude ${TRAEFIK_TUNNEL_BACKEND_SUBDOMAIN}.${TRAEFIK_TUNNEL_DOMAIN} so it can't steal traffic
      - "traefik.http.routers.${WARDEN_ENV_NAME}-nginx.rule=(HostRegexp(`{subdomain:.+}.${TRAEFIK_DOMAIN}`) || Host(`${TRAEFIK_DOMAIN}`) || HostRegexp(`${TRAEFIK_TUNNEL_BACKEND_SUBDOMAIN}.${TRAEFIK_TUNNEL_DOMAIN}`)) && !Host(`${TRAEFIK_TUNNEL_BACKEND_SUBDOMAIN}.${TRAEFIK_TUNNEL_DOMAIN}`)"
    volumes:
      - ./.warden/nginx-magento2.conf:/etc/nginx/available.d/magento2.conf

  php-fpm:
    volumes:
      - ../magento2-backend/deploy:/var/www/deploy/
    extra_hosts:
      - "${TRAEFIK_TUNNEL_BACKEND_SUBDOMAIN}.${TRAEFIK_TUNNEL_DOMAIN}:${TRAEFIK_ADDRESS:-0.0.0.0}"

  php-debug:
    volumes:
      - ../magento2-backend/deploy:/var/www/deploy/
    extra_hosts:
      - "${TRAEFIK_TUNNEL_BACKEND_SUBDOMAIN}.${TRAEFIK_TUNNEL_DOMAIN}:${TRAEFIK_ADDRESS:-0.0.0.0}"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

5 participants