> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monk.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Cloudflare DNS and Tunnel

> Managed DNS and Cloudflare Tunnel for secure connectivity.

## What is this integration?

Cloudflare provides globally distributed DNS, CDN, and edge services.

## What Monk manages

* DNS Zone and DNS Record
* Cloudflare Tunnel
* Cloudflare Tunnel Application

## Credentials

* `cloudflare-api-token`: Cloudflare API token with account/tunnel and DNS edit permissions
* `cloudflare-account-id`: Account tag/UUID from the dashboard URL (`/accounts/\<ACCOUNT_ID>`)
* `cloudflare-tunnel-token`: Tunnel token created by `cloudflare/cloudflare-tunnel` on first run (distinct from API token)

## Links

* Provider docs: [https://developers.cloudflare.com/dns/](https://developers.cloudflare.com/dns/)
* Tunnel API docs: [https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel-api/)

## Templates

### DNS record template

Save as `example-record.yaml` (see `src/cloudflare/example-record.yaml`):

```yaml theme={null}
namespace: cloudflare-examples

nginx:
  defines: runnable
  inherits: nginx/node-proxy

example-record:
  defines: cloudflare/cloudflare-dns-record
  zone_name: example.com
  name: www
  record_type: CNAME
  content: <- connection-domain-name("nginx")
  ttl: 1
  proxied: true
  services:
    data:
      protocol: custom
  connections:
    nginx:
      runnable: cloudflare-examples/nginx
      service: nginx
  depends:
    wait-for:
      runnables:
        - cloudflare-examples/nginx
      timeout: 60

stack:
  defines: group
  members:
    - cloudflare-examples/nginx
    - cloudflare-examples/example-record
```

### Tunnel: single app

Save as `example-tunnel.yaml` (see `src/cloudflare/example-tunnel.yaml`):

```yaml theme={null}
namespace: cloudflare-tunnel-example

app:
  defines: runnable
  containers:
    app:
      image: nginx:latest
  services:
    http:
      container: app
      port: 80
      protocol: tcp

tunnel:
  defines: cloudflare/cloudflare-tunnel
  account_id: <- secret("cloudflare-account-id")
  name: "example-tunnel"
  token_secret_ref: cloudflare-tunnel-token
  permitted-secrets:
    cloudflare-api-token: true
    cloudflare-tunnel-token: true
    cloudflare-account-id: true
  services:
    data:
      protocol: custom

# cloudflare-tunnel-token is created by the tunnel entity on first run (distinct from cloudflare-api-token)

tunnel-app:
  defines: cloudflare/cloudflare-tunnel-application
  account_id: <- secret("cloudflare-account-id")
  tunnel_id: <- connection-target("tunnel") entity-state get-member("id")
  zone_name: example.com
  hostname: app.example.com # use "@" for apex domain
  service: <- "http://" connection-ip("app") ":" connection-port("app") concat-all
  permitted-secrets:
    cloudflare-api-token: true
    cloudflare-account-id: true
  connections:
    tunnel:
      runnable: cloudflare-tunnel-example/tunnel
      service: data
    app:
      runnable: cloudflare-tunnel-example/app
      service: http
  depends:
    wait-for:
      runnables:
        - cloudflare-tunnel-example/tunnel
      timeout: 60

cloudflared:
  defines: runnable
  inherits: cloudflare/cloudflared
  connections:
    app:
      runnable: cloudflare-tunnel-example/app
      service: http
  depends:
    wait-for:
      runnables:
        - cloudflare-tunnel-example/tunnel
        - cloudflare-tunnel-example/app
      timeout: 60

stack:
  defines: group
  members:
    - cloudflare-tunnel-example/app
    - cloudflare-tunnel-example/tunnel
    - cloudflare-tunnel-example/tunnel-app
    - cloudflare-tunnel-example/cloudflared
```

### Tunnel: multiple apps through one tunnel

```yaml theme={null}
namespace: cloudflare-multi-app

tunnel:
  defines: cloudflare/cloudflare-tunnel
  account_id: <- secret("cloudflare-account-id")
  name: "multi-app-tunnel"
  token_secret_ref: cloudflare-tunnel-token
  permitted-secrets:
    cloudflare-api-token: true
    cloudflare-tunnel-token: true
    cloudflare-account-id: true
  services:
    data:
      protocol: custom

frontend:
  defines: runnable
  containers:
    app:
      image: nginx:latest
  services:
    http:
      container: app
      port: 3000
      protocol: tcp

frontend-tunnel:
  defines: cloudflare/cloudflare-tunnel-application
  account_id: <- secret("cloudflare-account-id")
  tunnel_id: <- connection-target("tunnel") entity-state get-member("id")
  zone_name: example.com
  hostname: app.example.com
  service: <- "http://" connection-ip("frontend") ":" connection-port("frontend") concat-all
  permitted-secrets:
    cloudflare-api-token: true
    cloudflare-account-id: true
  connections:
    tunnel:
      runnable: cloudflare-multi-app/tunnel
      service: data
    app:
      runnable: cloudflare-multi-app/frontend
      service: http

api:
  defines: runnable
  containers:
    app:
      image: nginx:latest
  services:
    http:
      container: app
      port: 8000
      protocol: tcp

api-tunnel:
  defines: cloudflare/cloudflare-tunnel-application
  account_id: <- secret("cloudflare-account-id")
  tunnel_id: <- connection-target("tunnel") entity-state get-member("id")
  zone_name: example.com
  hostname: api.example.com
  service: <- "http://" connection-ip("api") ":" connection-port("api") concat-all
  permitted-secrets:
    cloudflare-api-token: true
    cloudflare-account-id: true
  connections:
    tunnel:
      runnable: cloudflare-multi-app/tunnel
      service: data
    app:
      runnable: cloudflare-multi-app/api
      service: http

cloudflared:
  defines: runnable
  inherits: cloudflare/cloudflared
  connections:
    frontend:
      runnable: cloudflare-multi-app/frontend
      service: http
    api:
      runnable: cloudflare-multi-app/api
      service: http
  depends:
    wait-for:
      runnables:
        - cloudflare-multi-app/tunnel
      timeout: 60

stack:
  defines: group
  members:
    - cloudflare-multi-app/tunnel
    - cloudflare-multi-app/frontend
    - cloudflare-multi-app/frontend-tunnel
    - cloudflare-multi-app/api
    - cloudflare-multi-app/api-tunnel
    - cloudflare-multi-app/cloudflared
```

### Tunnel: cloudflared only (tunnel connector already exists)

Use this when you already have a tunnel token (in this case it's in global secret cloudflare-tunnel-token) and just need to run cloudflared. You must create the tunnel and DNS records manually in Cloudflare for the hostname(s) in your ingress. Each hostname should have a CNAME record pointing to `\<tunnel-id>.cfargotunnel.com`. For a full setup that creates tunnels and DNS records, use the `cloudflare/cloudflare-tunnel` and `cloudflare/cloudflare-tunnel-application` entities instead.

```yaml theme={null}
namespace: example

app:
  defines: runnable
  containers:
    app:
      image: nginx:latest
  services:
    http:
      container: app
      protocol: tcp
      port: 3000

cloudflare-tunnel:
  defines: runnable
  inherits: cloudflare/cloudflared
  files:
    config:
      container: cloudflared
      path: /etc/cloudflared/config.yml
      mode: 0644
      contents: |
        ingress:
          - hostname: mysite.example.com
            service: {{ v "app-url" }}
          - service: http_status:404
  connections:
    app:
      runnable: example/app
      service: http
  depends:
    wait-for:
      runnables:
        - example/app
      timeout: 60
  variables:
    token_secret_ref:
      type: string
      value: cloudflare-tunnel-token
    command_options:
      type: string
      value: --config /etc/cloudflared/config.yml --no-autoupdate # options after "tunnel"
    subcommand_options:
      type: string
      value: "" # options after "run" (e.g., --cred-file)
    app-host:
      type: string
      value: <- connection-ip("app")
    app-port:
      type: int
      value: <- connection-port("app")
    app-url:
      type: string
      value: <- "http://" $app-host ":" $app-port concat-all
  permitted-secrets:
    cloudflare-tunnel-token: true

stack:
  defines: group
  members:
    - example/app
    - example/cloudflare-tunnel
```

## Advanced Configuration Examples

### DNS Zone Management

```yaml theme={null}
namespace: cloudflare-dns-management

# Create a new DNS zone
my-zone:
  defines: cloudflare/cloudflare-dns-zone
  zone_name: myapp.com
  plan: free  # free, pro, business, enterprise

# Add multiple DNS records
www-record:
  defines: cloudflare/cloudflare-dns-record
  zone_name: myapp.com
  name: www
  record_type: CNAME
  content: myapp.com
  ttl: 1
  proxied: true

api-record:
  defines: cloudflare/cloudflare-dns-record
  zone_name: myapp.com
  name: api
  record_type: A
  content: 192.168.1.100
  ttl: 1
  proxied: false

# MX record for email
mx-record:
  defines: cloudflare/cloudflare-dns-record
  zone_name: myapp.com
  name: "@"
  record_type: MX
  content: mail.myapp.com
  priority: 10
  ttl: 1
```

### Page Rules for CDN Optimization

```yaml theme={null}
namespace: cloudflare-cdn

# Page rule for caching static assets
static-assets-rule:
  defines: cloudflare/cloudflare-page-rule
  zone_name: myapp.com
  target: "*myapp.com/static/*"
  actions:
    - id: cache_level
      value: cache_everything
    - id: browser_cache_ttl
      value: 31536000  # 1 year

# Page rule for API endpoints (no caching)
api-rule:
  defines: cloudflare/cloudflare-page-rule
  zone_name: myapp.com
  target: "*myapp.com/api/*"
  actions:
    - id: cache_level
      value: bypass
```

### Firewall Rules

```yaml theme={null}
namespace: cloudflare-security

# Block traffic from specific countries
geo-block-rule:
  defines: cloudflare/cloudflare-firewall-rule
  zone_name: myapp.com
  action: block
  filter:
    expression: "(ip.geoip.country in {\"CN\" \"RU\"})"

# Rate limiting for login endpoints
rate-limit-rule:
  defines: cloudflare/cloudflare-firewall-rule
  zone_name: myapp.com
  action: challenge
  filter:
    expression: "(http.request.uri.path contains \"/login\")"
  rate_limit:
    requests_per_period: 10
    period: 60
    action: challenge
```

## Troubleshooting

### Common Issues

**API token permission errors:**

```bash theme={null}
# Verify token has correct permissions in Cloudflare dashboard
# Ensure token has Zone:DNS:Edit and Zone:Page Rules:Edit permissions
```

**Zone not found errors:**

```bash theme={null}
# Check zone name spelling and that it exists in your Cloudflare account
# Verify the API token has access to the specified zone
```

**DNS propagation delays:**

* DNS changes can take up to 24 hours to propagate globally
* Use Cloudflare's development mode for testing to bypass cache

### Best Practices

1. **API Token Security**: Use restricted API tokens with minimal required permissions
2. **Zone Organization**: Group related DNS records in logical namespaces
3. **CDN Optimization**: Use page rules to optimize caching for different content types
4. **Security First**: Enable firewall rules and rate limiting for sensitive endpoints
5. **Monitoring**: Enable Cloudflare Analytics to monitor traffic patterns

## Integration with Other Services

Cloudflare works seamlessly with:

* **Monk Applications**: Proxy traffic through Cloudflare's global network
* **Load Balancers**: Distribute traffic across multiple origins
* **CDNs**: Combine with other CDNs for multi-layer caching
* **Security Tools**: Integrate with WAF and DDoS protection services
* **Analytics**: Feed traffic data into monitoring dashboards

## Support & Resources

* **Cloudflare Documentation**: [DNS API Reference](https://developers.cloudflare.com/dns/)
* **API Documentation**: [Cloudflare API v4](https://developers.cloudflare.com/api/)
* **Community**: [Cloudflare Community](https://community.cloudflare.com/)
* **Status Page**: [Cloudflare Status](https://www.cloudflarestatus.com/)
