Files
rick-infra/docs/caddy-service-configuration.md

7.0 KiB

Caddy Service Configuration

Overview

The Caddy role uses a simple file-based approach where services deploy configuration files to a sites-enabled directory. This follows standard nginx-like patterns and uses Caddy's import directive for automatic configuration loading.

Key Features

  • Simple & Reliable: File-based configuration with no API dependencies
  • Zero Downtime: Configuration reloads without service restarts
  • Automatic HTTPS: New domains get certificates automatically
  • Standard Pattern: Familiar nginx-like sites-enabled/ approach
  • Easy Debugging: Configuration files are plaintext and easily inspected
  • Version Control Friendly: Configuration changes are tracked in git

Architecture

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Service Role  │    │   sites-enabled/ │    │   Caddy Core    │
│   (Ansible)     │───▶│   *.caddy files  │───▶│   (Routes)      │
│                 │    │                  │    │                 │
└─────────────────┘    └──────────────────┘    └─────────────────┘

Usage Pattern

1. Service Role Structure

Create a service role with dependency on Caddy:

# roles/myservice/meta/main.yml
dependencies:
  - role: caddy

2. Service Configuration Template

Create a Caddy configuration template:

# roles/myservice/templates/myservice.caddy.j2
{{ service_domain }} {
    reverse_proxy {{ service_backend }}
}

3. Service Deployment

In your service tasks:

# roles/myservice/tasks/main.yml
- name: Deploy my service
  # ... service deployment tasks

- name: Deploy Caddy configuration
  template:
    src: myservice.caddy.j2
    dest: "{{ caddy_sites_enabled_dir }}/myservice.caddy"
    owner: root
    group: "{{ caddy_user }}"
    mode: '0644'
  notify: reload caddy

4. Advanced Configuration Example

For load balancing and custom headers:

# roles/myapi/templates/myapi.caddy.j2
{{ service_domain }} {
    reverse_proxy localhost:8080 localhost:8081 {
        lb_policy round_robin
        header_up Host {upstream_hostport}
        header_up X-Real-IP {remote_host}
        header_up X-Custom-Header "MyValue"
    }
}

5. Service Removal

To remove a service:

- name: Remove service configuration
  file:
    path: "{{ caddy_sites_enabled_dir }}/myservice.caddy"
    state: absent
  notify: reload caddy

Available Handlers

reload caddy

  • Reloads Caddy configuration without restart
  • Automatically triggered when .caddy files change
  • Safe to run multiple times

Configuration Variables

Template Variables

Variable Required Description Example
service_domain Yes Domain for the service "api.jnss.me"
service_backend Yes Backend address "localhost:8080"

Caddy Configuration

Variable Default Description
caddy_sites_enabled_dir "/etc/caddy/sites-enabled" Directory for service configs
caddy_user "caddy" Caddy system user
caddy_config_file "/etc/caddy/Caddyfile" Main Caddyfile path

Security

  • File Permissions: Configuration files owned by root:caddy (0644)
  • Process Isolation: SystemD security restrictions
  • Network Isolation: Firewall only opens 80/443
  • Configuration Validation: Automatic syntax checking

Examples

Simple API Service

# roles/simple-api/templates/simple-api.caddy.j2
simple.jnss.me {
    reverse_proxy localhost:3000
}
# roles/simple-api/tasks/main.yml
- name: Deploy simple API configuration
  template:
    src: simple-api.caddy.j2
    dest: "{{ caddy_sites_enabled_dir }}/simple-api.caddy"
    owner: root
    group: "{{ caddy_user }}"
    mode: '0644'
  notify: reload caddy

Load Balanced Service

# roles/balanced-api/templates/balanced-api.caddy.j2
balanced.jnss.me {
    reverse_proxy localhost:8080 localhost:8081 localhost:8082 {
        lb_policy least_conn
        health_timeout 5s
        health_interval 30s
    }
}

Static File Service

# roles/static-files/templates/static-files.caddy.j2
static.jnss.me {
    root * /var/www/static
    file_server browse
}

API with Custom Headers

# roles/api-service/templates/api-service.caddy.j2
api.jnss.me {
    reverse_proxy localhost:8080 {
        header_up Host {upstream_hostport}
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-Proto https
        header_down -Server
    }
}

Troubleshooting

List Service Configurations

ls -la /etc/caddy/sites-enabled/

Check Configuration Files

cat /etc/caddy/sites-enabled/myservice.caddy

Validate Caddy Configuration

caddy validate --config /etc/caddy/Caddyfile

Test Configuration Changes

caddy fmt --overwrite /etc/caddy/Caddyfile

View Caddy Logs

journalctl -u caddy -f

Reload Configuration

systemctl reload caddy

Test Service Directly

curl http://localhost:8080/health

Test Through Caddy

curl https://myservice.jnss.me/health

Directory Structure

After deployment, your configuration will look like:

/etc/caddy/
├── Caddyfile                 # Main config with "import sites-enabled/*"
└── sites-enabled/            # Service configurations
    ├── api.caddy            # API service config
    ├── dashboard.caddy      # Dashboard service config  
    └── static.caddy         # Static files config

Best Practices

  1. Naming: Use descriptive, unique configuration file names
  2. Domains: Follow consistent domain patterns (e.g., service.domain.com)
  3. Templates: Use Jinja2 variables for flexibility
  4. Health Checks: Always include health check endpoints in services
  5. Dependencies: Declare Caddy dependency in service role meta
  6. Testing: Validate configuration before deployment
  7. Cleanup: Remove .caddy files when removing services
  8. Version Control: Track all configuration changes in git

Service Deployment Checklist

  • Create service role with Caddy dependency
  • Create .caddy.j2 template file
  • Deploy template to {{ caddy_sites_enabled_dir }}/
  • Set proper file permissions (root:caddy 0644)
  • Trigger reload caddy handler
  • Test service accessibility
  • Verify HTTPS certificate generation

Support

For issues or questions about the Caddy service configuration system:

  1. Check Caddy logs: journalctl -u caddy -f
  2. Validate configuration: caddy validate --config /etc/caddy/Caddyfile
  3. Check file permissions: ls -la /etc/caddy/sites-enabled/
  4. Test service directly before adding to Caddy
  5. Use ansible-playbook --check for dry-run testing