Files
homelab-opentofu/modules/01-networking/caddy-proxy/README.md
Yuris Cakranegara af038e23ea feat: add caddy proxy
2025-06-12 20:55:58 +10:00

4.9 KiB

Caddy Proxy Module

This module creates a Caddy reverse proxy server that dynamically configures itself based on service definitions passed to it.

Overview

The Caddy Proxy module:

  • Accepts service definitions that specify whether to expose them via reverse proxy
  • Dynamically generates Caddyfile configuration from these service definitions
  • Supports custom Caddy configuration blocks per service
  • Deploys a Caddy container with the generated configuration
  • Manages TLS certificates automatically using Let's Encrypt
  • Creates DNS records for services with configurable Cloudflare proxying settings

Usage

Basic Integration

Add the module to your main Terraform configuration:

module "homelab_caddy_proxy" {
  source             = "./modules/01-networking/caddy-proxy"
  domain             = "yourdomain.com"
  tls_email          = "your-email@example.com"  # For Let's Encrypt
  container_name     = "caddy-proxy"
  cloudflare_zone_id = module.cloudflare_globals.cloudflare_zone_id
  external_ip        = module.cloudflare_globals.external_ip
  service_definitions = module.services.service_definitions
  networks           = ["your-docker-network"]
  monitoring         = true
}

Service Definition Format

Services should include the following fields to be properly exposed through Caddy:

{
  name       = "service-name"
  endpoint   = "service-container:port"
  subdomains = ["app", "dashboard"]  # Will create app.yourdomain.com, dashboard.yourdomain.com
  
  # Specify how to publish this service: "tunnel", "reverse_proxy", or "both" (default)
  publish_via = "both"
  
  # Control whether the DNS record is proxied through Cloudflare (default: true)
  proxied = true
  
  # Option 1: Simplified Caddy configuration via options
  caddy_options = {
    "health_path" = "/health"
    "health_interval" = "30s"
    "header_up X-Real-IP" = "{http.request.remote}"
    # Additional reverse_proxy options as needed
  }
  
  # Option 2: Full custom Caddy configuration (takes precedence if both are provided)
  caddy_config = <<-EOT
    # Raw Caddy configuration goes here
    reverse_proxy /api/* api-backend:8080
    reverse_proxy /* frontend:3000
    header X-Powered-By "My Awesome Homelab"
    log {
      output file /var/log/access.log
    }
  EOT
}

The publish_via field controls which networking module(s) will expose the service:

  • "tunnel": Service will only be published via Cloudflare tunnel
  • "reverse_proxy": Service will only be exposed via Caddy reverse proxy
  • "both": Service will be published via both methods (default)

Variables

Variable Description Type Default
container_name The name of the Caddy container string "" (generates "caddy-proxy")
image_tag The tag of the Caddy Docker image to use string "latest"
domain The domain name to use for services string -
tls_email Email address for Let's Encrypt string -
service_definitions List of service definitions to evaluate list(object) -
networks List of Docker networks to connect to list(string) []
monitoring Whether to enable monitoring for the container bool false
cloudflare_zone_id Cloudflare Zone ID for creating DNS records string ""
external_ip External IP address for A records string ""

Outputs

Output Description
container_name The name of the deployed Caddy container
config_hash The SHA256 hash of the generated Caddyfile content
service_sites Map of generated Caddy site configurations

Example Service Integration

Basic Service with Default Settings

# Example based on ntfy (reverse-proxy only with direct IP exposure)
output "service_definition" {
  description = "Service definition for a notification service"
  value = {
    name         = "ntfy"
    primary_port = 80
    endpoint     = "http://ntfy:80"
    subdomains   = ["ntfy"]
    publish_via  = "reverse_proxy"  # Only expose via Caddy reverse proxy
    proxied      = false            # Don't proxy through Cloudflare (expose direct IP)
  }
}

Service with Custom Caddy Configuration

# Example showing a service with custom Caddy configuration
output "service_definition" {
  description = "Service definition with custom Caddy configuration"
  value = {
    name         = "custom-service"
    primary_port = 8080
    endpoint     = "http://custom-service:8080"
    subdomains   = ["custom"]
    publish_via  = "reverse_proxy"
    proxied      = true  # Use Cloudflare proxying (default)
    caddy_config = <<-EOT
      # Handle API requests specially
      handle /api/* {
        reverse_proxy custom-service:8080 {
          header_up X-Real-IP {remote}
        }
      }
      
      # Handle all other requests
      handle {
        reverse_proxy custom-service:8080
        header +Access-Control-Allow-Origin "*"
      }
    EOT
  }
}