https://github.com/hhftechnology/pangolin-cloudflare-tunnel
A bridge between Traefik and Cloudflare Zero-Trust tunnels
https://github.com/hhftechnology/pangolin-cloudflare-tunnel
cloudflare-tunnels cloudflared pangolin traefik tunnels
Last synced: 14 days ago
JSON representation
A bridge between Traefik and Cloudflare Zero-Trust tunnels
- Host: GitHub
- URL: https://github.com/hhftechnology/pangolin-cloudflare-tunnel
- Owner: hhftechnology
- Created: 2025-04-05T10:18:32.000Z (12 months ago)
- Default Branch: main
- Last Pushed: 2025-12-30T17:43:18.000Z (3 months ago)
- Last Synced: 2026-01-03T13:47:38.491Z (3 months ago)
- Topics: cloudflare-tunnels, cloudflared, pangolin, traefik, tunnels
- Language: Go
- Homepage: https://forum.hhf.technology/t/setting-up-cloudflare-tunnels-with-pangolin/
- Size: 184 KB
- Stars: 65
- Watchers: 2
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
Pangolin-Cloudflare-Tunnel
A bridge between Traefik and Cloudflare Zero-Trust tunnels that enables Pangolin users to leverage Cloudflare's global network.
[](https://hub.docker.com/r/hhftechnology/pangolin-cloudflare-tunnel)

[](https://discord.gg/HDCt9MjyMJ)
## Overview
This tool synchronizes Traefik routes with Cloudflare Zero-Trust tunnels, providing an alternative or complementary tunneling option for Pangolin deployments. This integration allows you to:
- Expose Pangolin-managed services through Cloudflare's global network
- Take advantage of Cloudflare's DDoS protection and caching capabilities
- Provide an alternative remote access method alongside Pangolin's WireGuard tunnels
- **NEW**: Manage multiple domains across different Cloudflare zones
- **NEW**: Exclude specific resources from Cloudflare tunneling
- **NEW**: Automatic cleanup of DNS records for deleted resources
## Features
- **Multi-Domain/Multi-Zone Support**: Configure multiple domains across different Cloudflare zones
- **Resource Exclusion**: Ignore specific domains or patterns (e.g., Jellyfin for TOS compliance)
- **Automatic DNS Cleanup**: Automatically remove DNS records when resources are deleted
- **TLS Route Filtering**: Optionally skip or include TLS-enabled routes
- **Multiple Entrypoints**: Support for multiple Traefik entrypoints
- **Automatic Synchronization**: Real-time sync between Traefik and Cloudflare
- **Robust Error Handling**: Retry logic with exponential backoff
- **Structured Logging**: Comprehensive logging with configurable verbosity
## Integration with Pangolin
When used with Pangolin:
1. Pangolin manages your internal resources
2. Traefik (used by Pangolin) handles the local routing
3. This tool synchronizes Traefik routes to Cloudflare tunnels
4. Cloudflare provides an additional layer of protection and global distribution
This creates a combination where you can use Pangolin for secure local deployment via Cloudflare tunnels for public-facing services for Unraid/NAS users without opening ports or buying a VPS.
## Configuration
### Required Environment Variables
| Environment Variable | Type | Description |
| :----------------------- | ------ | ------------------------------------------------------------ |
| CLOUDFLARED_TOKEN | String | Token for the `cloudflared` daemon. This is the token provided after [creating a tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/#1-create-a-tunnel). |
| CLOUDFLARE_API_TOKEN | String | A valid [Cloudflare API token](https://dash.cloudflare.com/profile/api-tokens) |
| CLOUDFLARE_ACCOUNT_ID | String | Your account ID. Available in the URL at https://dash.cloudflare.com |
| CLOUDFLARE_TUNNEL_ID | String | The ID of your Cloudflare tunnel |
| TRAEFIK_API_ENDPOINT | String | The HTTP URI to Traefik's API (e.g., http://traefik:8080) |
| TRAEFIK_SERVICE_ENDPOINT | String | The HTTP URI to Traefik's web entrypoint (e.g., https://traefik:443) |
### Zone Configuration (Choose One)
#### Single Zone (Legacy)
| Environment Variable | Type | Description |
| :----------------------- | ------ | ------------------------------------------------------------ |
| CLOUDFLARE_ZONE_ID | String | The Cloudflare zone ID of your site |
| DOMAIN_NAME | String | (Optional) The domain name used for this zone |
#### Multi-Zone (NEW)
| Environment Variable | Type | Description |
| :----------------------- | ------ | ------------------------------------------------------------ |
| CLOUDFLARE_ZONE_IDS | String | Comma-separated list of Cloudflare zone IDs (e.g., `zone1,zone2,zone3`) |
| DOMAIN_NAMES | String | Comma-separated list of domain names matching the zones (e.g., `example.com,test.com,demo.com`) |
**Note**: The order of zone IDs must match the order of domain names.
### Entrypoint Configuration (Choose One)
| Environment Variable | Type | Description |
| :----------------------- | ------ | ------------------------------------------------------------ |
| TRAEFIK_ENTRYPOINTS | String | Comma-separated list of Traefik entrypoints (e.g., `web,websecure`) |
| TRAEFIK_ENTRYPOINT | String | (Legacy) Single Traefik entrypoint (e.g., `web`) |
### Optional Environment Variables
| Environment Variable | Type | Default | Description |
| :------------------- | ------- | ------- | ------------------------------------------------------------ |
| SKIP_TLS_ROUTES | Boolean | `true` | Skip routes with TLS configured. Set to `false` to include TLS routes |
| POLL_INTERVAL | String | `10s` | Polling interval (e.g., `10s`, `1m`, `30s`) |
| LOG_LEVEL | String | `info` | Log level (`debug` or `info`) |
| IGNORE_PATTERNS | String | (empty) | Comma-separated regex patterns for domains to ignore (e.g., `^jellyfin\.,^media\.`) |
| ENABLE_DNS_CLEANUP | Boolean | `true` | Automatically remove DNS records for deleted resources |
### Cloudflare Permissions
The `CLOUDFLARE_API_TOKEN` is your API token which can be created at: https://dash.cloudflare.com/profile/api-tokens
Ensure the permissions for your Cloudflare token match the following:
- Account -> Cloudflare Tunnel -> Edit
- Account -> Zero Trust -> Edit
- User -> User Details -> Read
- Zone -> DNS -> Edit
## Example with Pangolin
This example shows how to integrate Cloudflare tunnels with a Pangolin deployment.
1. First, set up Pangolin according to its [installation guide](https://docs.fossorial.io/Getting%20Started/quick-install)
2. Create an `.env` file with your Cloudflare credentials:
```bash
cd example
cp .env.example .env
vi .env
```
3. Add this service to your existing Pangolin `docker-compose.yml`:
```yaml
name: pangolin
services:
pangolin:
image: fosrl/pangolin:1.1.0
container_name: pangolin
restart: unless-stopped
volumes:
- ./config:/app/config
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
interval: "3s"
timeout: "3s"
retries: 5
networks:
- pangolin_network
traefik:
image: traefik:v3.3.3
container_name: traefik
restart: unless-stopped
ports:
- 443:443
- 80:80
- 8080:8080
depends_on:
pangolin:
condition: service_healthy
command:
- --configFile=/etc/traefik/traefik_config.yml
environment:
- CLOUDFLARE_DNS_API_TOKEN=your_dns_api_token_here
volumes:
- ./config/traefik:/etc/traefik:ro
- ./config/letsencrypt:/letsencrypt
- ./config/traefik/logs:/var/log/traefik
networks:
- pangolin_network
cloudflared:
image: cloudflare/cloudflared:2025.4.0
container_name: cloudflared
restart: unless-stopped
command:
- tunnel
- --no-autoupdate
- run
- --token=your_cloudflared_token_here
networks:
- pangolin_network
depends_on:
- traefik
traefik-cloudflare-tunnel:
image: "hhftechnology/pangolin-cloudflare-tunnel:latest"
container_name: pangolin-cloudflare-tunnel
restart: unless-stopped
environment:
# Required Configuration
- CLOUDFLARE_API_TOKEN=your_api_token_here
- CLOUDFLARE_ACCOUNT_ID=your_account_id_here
- CLOUDFLARE_TUNNEL_ID=your_tunnel_id_here
- TRAEFIK_SERVICE_ENDPOINT=https://traefik:443
- TRAEFIK_API_ENDPOINT=http://traefik:8080
- TRAEFIK_ENTRYPOINTS=web,websecure
# Multi-Zone Configuration (NEW)
- CLOUDFLARE_ZONE_IDS=zone_id_1,zone_id_2
- DOMAIN_NAMES=example.com,test.com
# Optional Configuration
- POLL_INTERVAL=10s
- SKIP_TLS_ROUTES=false
- LOG_LEVEL=debug
- ENABLE_DNS_CLEANUP=true
# Resource Exclusion (NEW) - Ignore Jellyfin and media services
- IGNORE_PATTERNS=^jellyfin\.,^media\.
networks:
- pangolin_network
depends_on:
- traefik
- cloudflared
networks:
pangolin_network:
driver: bridge
name: pangolin_network
```
4. Restart your Pangolin stack:
```bash
sudo docker compose up -d
```
5. Create resources in Pangolin as usual. Resources with the specified entrypoint will be automatically exposed through Cloudflare tunnels.
## Use Cases
### Multi-Domain Setup
Manage multiple domains across different Cloudflare zones:
```bash
CLOUDFLARE_ZONE_IDS=zone1,zone2,zone3
DOMAIN_NAMES=example.com,test.com,demo.com
```
### Resource Exclusion
Exclude specific services that shouldn't use Cloudflare CDN (e.g., Jellyfin for TOS compliance):
```bash
IGNORE_PATTERNS=^jellyfin\.,^media\.,^plex\.
```
This will exclude:
- `jellyfin.example.com`
- `media.example.com`
- `plex.example.com`
### Automatic Cleanup
Enable automatic DNS cleanup to remove records for deleted resources:
```bash
ENABLE_DNS_CLEANUP=true
```
When a resource is deleted from Traefik, its corresponding DNS record will be automatically removed from Cloudflare.
## Architecture
The application is structured with a clean, modular architecture:
```
.
├── main.go # Application entry point
├── internal/
│ ├── config/ # Configuration management
│ ├── traefik/ # Traefik API client and router management
│ ├── cloudflare/ # Cloudflare API client and operations
│ ├── sync/ # Synchronization orchestration
│ └── errors/ # Custom error types
└── pkg/
└── retry/ # Retry utility with exponential backoff
```
## Advanced Configuration
### Multi-Host Setup with Gerbil
You can use this tool with multiple Docker hosts on the same hypervisor layer while keeping Gerbil in your setup. This allows connecting multiple LXC containers (each running Docker) to a single centralized Pangolin instance.
### Custom Polling Intervals
Adjust the polling interval based on your needs:
```bash
POLL_INTERVAL=30s # Less frequent polling (lower resource usage)
POLL_INTERVAL=5s # More frequent polling (faster updates)
```
### Debug Logging
Enable debug logging for troubleshooting:
```bash
LOG_LEVEL=debug
```
## Troubleshooting
### DNS Records Not Created
1. Check that your `CLOUDFLARE_API_TOKEN` has the correct permissions
2. Verify that `CLOUDFLARE_ZONE_IDS` and `DOMAIN_NAMES` match correctly
3. Enable debug logging to see detailed error messages
### Domain Not Matching Zone
If you see warnings like "no matching zone", ensure:
- The domain is a subdomain of one of your configured `DOMAIN_NAMES`
- The order of `CLOUDFLARE_ZONE_IDS` matches `DOMAIN_NAMES`
### Resources Not Excluded
If the `IGNORE_PATTERNS` aren't working:
- Check that your regex patterns are correct
- Test patterns at https://regex101.com
- Remember to escape special characters (e.g., `\.` for literal dots)
## For More Information
- [Pangolin Documentation](https://docs.fossorial.io/Pangolin/)
- [Cloudflare Tunnel Documentation](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/)
- [Traefik Documentation](https://doc.traefik.io/traefik/)
## Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
## License
This project follows the MIT license.