Skip to main content

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)

Templates

DNS record template

Save as example-record.yaml (see src/cloudflare/example-record.yaml):
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):
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

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.
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

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

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

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:
# Verify token has correct permissions in Cloudflare dashboard
# Ensure token has Zone:DNS:Edit and Zone:Page Rules:Edit permissions
Zone not found errors:
# 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