Skip to content

Conversation

@thesteganos
Copy link

This commit introduces Cloudflare Tunnel (cloudflared) integration to allow you to easily expose your self-hosted services to the internet.

Key changes:

  • Added a cloudflared service to docker-compose.yml. This service uses the cloudflare/cloudflared:latest image and is configured via a CLOUDFLARED_TOKEN environment variable. It depends on the Caddy service.
  • Verified that the existing Caddyfile configuration is suitable for acting as an origin for Cloudflare Tunnel. Caddy will continue to manage local SSL and route traffic to backend applications as configured by its hostname variables (e.g., N8N_HOSTNAME, WEBUI_HOSTNAME).
  • Updated README.md with a new "Cloudflare Tunnel Integration" section. This section explains:
    • How to obtain and set the CLOUDFLARED_TOKEN.
    • How to configure the Cloudflare Tunnel in the Cloudflare dashboard to point to the Caddy service (typically https://host.docker.internal:443 or http://host.docker.internal:<caddy_http_port>).
    • That the relevant Caddy hostname variable (e.g., N8N_HOSTNAME) must be set to the public domain being tunneled.
  • Added CLOUDFLARED_TOKEN to .env.example with an explanatory comment.

This integration provides a secure and convenient way for you to access your services remotely without needing to configure complex network settings or expose ports directly.

This commit introduces Cloudflare Tunnel (cloudflared) integration to allow you to easily expose your self-hosted services to the internet.

Key changes:

- Added a `cloudflared` service to `docker-compose.yml`. This service uses the `cloudflare/cloudflared:latest` image and is configured via a `CLOUDFLARED_TOKEN` environment variable. It depends on the Caddy service.
- Verified that the existing `Caddyfile` configuration is suitable for acting as an origin for Cloudflare Tunnel. Caddy will continue to manage local SSL and route traffic to backend applications as configured by its hostname variables (e.g., `N8N_HOSTNAME`, `WEBUI_HOSTNAME`).
- Updated `README.md` with a new "Cloudflare Tunnel Integration" section. This section explains:
    - How to obtain and set the `CLOUDFLARED_TOKEN`.
    - How to configure the Cloudflare Tunnel in the Cloudflare dashboard to point to the Caddy service (typically `https://host.docker.internal:443` or `http://host.docker.internal:<caddy_http_port>`).
    - That the relevant Caddy hostname variable (e.g., `N8N_HOSTNAME`) must be set to the public domain being tunneled.
- Added `CLOUDFLARED_TOKEN` to `.env.example` with an explanatory comment.

This integration provides a secure and convenient way for you to access your services remotely without needing to configure complex network settings or expose ports directly.
@leex279
Copy link
Collaborator

leex279 commented May 31, 2025

Thanks, looks good, I will take a look as soon as this is done: #72

@coleam00 coleam00 added the enhancement New feature or request label Jun 10, 2025
@igormf
Copy link

igormf commented Jul 9, 2025

Hi everyone,

I ran into an issue where this commit setup fails in the newest "main" branch. Caddy can't get an SSL certificate from Let's Encrypt, leading to errors. After some infinite loops, I got it working perfectly.

This guide explains how to properly configure Caddy to work behind a Cloudflare Tunnel using a free Cloudflare Origin Certificate.

The Problem: Why It Fails

The core issue is how Caddy and Cloudflare Tunnels interact with SSL validation:

  1. Caddy's Default Method: By default, Caddy tries to get a certificate from Let's Encrypt using an HTTP-01 challenge. This requires the Let's Encrypt server to be able to reach your server directly via a public IP address (an A or AAAA DNS record).
  2. Cloudflare Tunnel's Method: A tunnel works by creating an outbound connection from your server to Cloudflare. Your public DNS record is a CNAME pointing to a Cloudflare service, not your server's IP.
  3. The Conflict: When Let's Encrypt tries to validate your domain, it follows the CNAME to Cloudflare's edge, not your Caddy instance. The validation fails, and Caddy falls back to serving an untrusted self-signed certificate.

The Solution: Use a Cloudflare Origin Certificate

The correct approach is to let Cloudflare handle the public-facing SSL and use a special, free Origin Certificate to secure the connection between Cloudflare's edge and your local Caddy server.

Here's the step-by-step guide:

Step 1: Create a Cloudflare Origin Certificate

  1. In your Cloudflare Dashboard, go to SSL/TLS -> Origin Server.
  2. Click Create Certificate.
  3. Leave the defaults, but make sure your domain and a wildcard version are listed (e.g., yourdomain.com and *.yourdomain.com).
  4. Click Create. Cloudflare will show you an Origin Certificate and a Private Key.
  5. Copy the contents of the "Origin Certificate" into a file named origin.pem.
  6. Copy the contents of the "Private Key" into a file named origin.key.

Step 2: Configure Your Project

First, place your new certificate files in a directory that Docker can access. A good structure is:

your-project/
├── caddy/
│   ├── certs/
│   │   ├── origin.key
│   │   └── origin.pem
├── Caddyfile
└── docker-compose.yml

Next, ensure your docker-compose.yml mounts these certificates into the Caddy container.

In docker-compose.yml:

services:
  caddy:
    # ... your other caddy settings
    volumes:
      # ... other volumes
      - ./caddy/certs:/etc/caddy/certs:ro # <-- Add this line

Step 3: Update Your Caddyfile

Now, tell Caddy to stop trying to use Let's Encrypt and instead use the files you provided for your public-facing services.

In Caddyfile:

# Example for one service. Repeat for all public services.
{$N8N_HOSTNAME} {
    # This tells Caddy to use the mounted Cloudflare Origin Certificate
    # instead of trying to fetch one from Let's Encrypt.
    tls /etc/caddy/certs/origin.pem /etc/caddy/certs/origin.key

    # Reverse proxy to your internal service
    reverse_proxy n8n:5678
}

Step 4: Configure the Cloudflare Tunnel

This is the final, crucial piece. You need to tell your tunnel how to correctly connect to Caddy over HTTPS.

  1. In the Cloudflare Zero Trust Dashboard, go to Access -> Tunnels.
  2. Configure your tunnel and go to the Public Hostnames tab.
  3. Edit the hostname you want to configure (e.g., n8n.yourdomain.com).
  4. Set the following:
    • Service Type: HTTPS
    • URL: host.docker.internal (substitute for localhost, either that or caddy:443 since my cloudflared is inside the same container).
  5. Expand Additional application settings -> TLS.
  6. In the Origin Server Name field, enter the full public hostname (e.g., n8n.yourdomain.com). This tells Cloudflare which "resident" to ask for when it connects to Caddy, preventing an SNI mismatch error.
  7. Uncheck the box for "No TLS Verify". With an Origin Certificate, this is not needed and disabling verification is less secure.
  8. Save the hostname. Repeat for all other public services.

After these steps, your setup will be secure and fully functional. The browser sees a valid Cloudflare certificate, and the connection from Cloudflare to your local machine is encrypted and verified using the Origin Certificate.


I hope this helps others who want to run this amazing stack locally without exposing ports or paying for a cloud server.

A big thank you to @coleam00 for the incredible package! This "local-first" approach is powerful. Adding tunnel support makes it accessible to even more people, especially those behind restrictive networks without port forwarding. Great work. Also thank you @thesteganos for the original code, it took me down the correct rabbit hole.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants