Files
homelab-opentofu/modules/01-networking/cloudflared-tunnel

Cloudflare Tunnel Module

This module creates and manages Cloudflare Tunnels using OpenTofu, automating the entire setup process including:

  1. Creating the Cloudflare tunnel
  2. Configuring tunnel routing rules
  3. Setting up DNS records
  4. Deploying the cloudflared tunnel container

Features

  • Automated Tunnel Management: Creates and configures Cloudflare tunnels via the API
  • Multiple Service Support: Route multiple applications through a single tunnel
  • DNS Management: Automatically creates DNS records for your applications
  • Docker Integration: Deploys the cloudflared container with proper configuration
  • Secret Management: Auto-generates tunnel secrets if not provided

Prerequisites

Before using this module, you need:

  1. A Cloudflare account
  2. API token with the following permissions:
    • Account.Cloudflare Tunnel:Edit
    • Zone.DNS:Edit
    • Zone.Zone:Read
  3. Your Cloudflare account ID and zone ID

Usage

module "homelab_tunnel" {
  source = "./modules/01-networking/cloudflared-tunnel"

  cloudflare_account_id = var.cloudflare_account_id
  cloudflare_zone_id    = var.cloudflare_zone_id

  tunnel_name    = "homelab-tunnel"
  container_name = "cloudflared-homelab"

  ingress_rules = [
    {
      hostname = "dashboard.example.com"
      service  = "http://homepage:3000"
    }
  ]

  service_definitions = [
    {
      name = "homepage"
      subdomains = ["dashboard"]
      endpoint = "http://homepage:3000"
    }
  ]
}

Connecting with the Cloudflare Globals Module

For cleaner code organization, use the globals module:

module "cloudflare_globals" {
  source = "./modules/00-globals/cloudflare"
}

module "homelab_tunnel" {
  source = "./modules/01-networking/cloudflared-tunnel"

  cloudflare_account_id = module.cloudflare_globals.cloudflare_account_id
  cloudflare_zone_id    = module.cloudflare_globals.cloudflare_zone_id
  tunnel_name = "homelab-tunnel"
  ingress_rules = [
    {
      hostname = "media.${module.cloudflare_globals.domain}"
      service  = "http://jellyfin:8096"
    }
  ]
}

Variables

Name Description Type Default
cloudflare_account_id Cloudflare account ID string (required)
cloudflare_zone_id Cloudflare zone ID for your domain string (required)
container_name Name of the Cloudflare tunnel container string "" (defaults to "cloudflared-{tunnel_name}")
image_tag Docker image tag for cloudflared string "latest"
tunnel_name Name of the tunnel string (required)
tunnel_secret Secret for the tunnel string "" (auto-generated if empty)
ingress_rules List of ingress rules list(object) (optional)
service_definitions List of service definitions. Tunnel will create DNS records for each service with a subdomain. list(object) (optional)
monitoring Enable monitoring via Watchtower bool true

Ingress Rules Object Structure

ingress_rules = [
  {
    hostname = "app.example.com"         # FQDN for the service
    service = "http://container:port"    # Internal service URL
    path = "/api/*"                      # Optional path pattern
    create_dns_record = true             # Whether to create DNS record (default: true)
  }
]

Outputs

Name Description
tunnel_id ID of the created tunnel
tunnel_name Name of the tunnel
tunnel_token Token for the tunnel (sensitive)
cname_target CNAME target for the tunnel
dns_records Map of created DNS records
container_name Name of the cloudflared container
container_id ID of the cloudflared container
image_id ID of the cloudflared image
ip_address IP address of the cloudflared container