caddy-docker-proxy

repository·master·Indexed 26 days ago

https://github.com/lucaslorentz/caddy-docker-proxy

A Caddy plugin that automates reverse proxy configuration by scanning Docker labels to dynamically generate a Caddyfile and perform zero-downtime reloads. It supports standalone, controller, and server execution modes, and provides a Go Client interface for interacting with Docker and Swarm resources.

Tokens
3.4K
Snippets
5
Records
18
Agent score
89%

What's inside caddy-docker-proxy

  1. Quickstart: Setup Caddy-Docker-Proxy with Docker Compose

    master

    To use Caddy-Docker-Proxy, you need to create a dedicated Docker network (ideally with IPv6 support to ensure correct client IP visibility) and run the Caddy container with access to the Docker socket.

    1. Create the network: docker network create caddy --ipv6

    2. Configure the Caddy service in compose.yaml:

      • Use the lucaslorentz/caddy-docker-proxy:ci-alpine image.
      • Map ports 80, 443 (TCP), and 443 (UDP).
      • Set CADDY_INGRESS_NETWORKS=caddy to tell the plugin which network to monitor for upstreams.
      • Mount /var/run/docker.sock to allow the plugin to scan Docker metadata.
      • Mount a volume for Caddy data (e.g., caddy_data:/data) to persist certificates.
    3. Configure your application service (e.g., whoami) to be proxied:

      • Connect it to the caddy network.
      • Add labels starting with caddy to define the domain and proxy behavior.
    ### caddy/compose.yaml
    services:
      caddy:
        image: lucaslorentz/caddy-docker-proxy:ci-alpine
        ports:
          - 80:80
          - 443:443/tcp
          - 443:443/udp
        environment:
          - CADDY_INGRESS_NETWORKS=caddy
        networks:
          - caddy
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
          - caddy_data:/data
        restart: unless-stopped
    
    networks:
      caddy:
        external: true
    
    volumes:
      caddy_data: {}
    
    ### whoami/compose.yaml
    services:
      whoami:
        image: traefik/whoami
        networks:
          - caddy
        labels:
          caddy: whoami.example.com
          caddy.reverse_proxy: "{{upstreams 80}}"
    
    networks:
      caddy:
        external: true
  2. Ensure persistent storage for Caddy certificates

    master
    In production Docker Swarm clusters, you must map a persistent Docker volume to the /data folder. This prevents Caddy from re-issuing certificates every time it restarts, which would exceed Let's Encrypt quotas. For resilient deployments with multiple replicas, use a volume that supports multiple mounts (e.g., Network File Sharing).
  3. Configure Caddy-Docker-Proxy Execution Modes

    master

    You can run Caddy-Docker-Proxy in three distinct modes using the --mode flag or CADDY_DOCKER_MODE environment variable:

    • Standalone (default): Runs both the controller and the server in a single instance. No additional configuration required.
    • Controller: Monitors the Docker cluster, generates configuration, and pushes it to servers. Requires access to the Docker host socket. If connected to multiple networks, define the controller network via --controller-network or CADDY_CONTROLLER_NETWORK.
    • Server: Acts as a proxy but does not generate configuration itself; it waits for a controller. To be discoverable, mark it with the label caddy_controlled_server and define the controller network via --controller-network or CADDY_CONTROLLER_NETWORK. Servers do not need access to the Docker host socket and can run on worker nodes.
  4. Proxy Swarm Services vs Raw Containers

    master

    Caddy-Docker-Proxy can proxy to both Docker Swarm services and individual containers. The target type is determined by where you place your labels in your compose.yaml:

    • Swarm Services: Place labels inside the deploy block. Caddy will use the service DNS name or all service task IPs (controlled by proxy-service-tasks).
    • Containers: Place labels outside the deploy block (at the service level).
    ### Services (Swarm)
    ```yml
    services:
      foo:
        deploy:
          labels:
            caddy: service.example.com
            caddy.reverse_proxy: {{upstreams}}

    Containers

    services:
      foo:
        labels:
          caddy: service.example.com
          caddy.reverse_proxy: {{upstreams}}
  5. Convert Docker Labels to Caddyfile Configuration

    master

    Any Docker label prefixed with caddy is converted into a Caddyfile configuration.

    Syntax Rules

    • Directives and Arguments: The label key is the directive name, and the value contains whitespace-separated arguments. Use double-quotes ("") or backticks ( ) for arguments containing spaces or newlines.
    • Nesting: Dots (.) in label keys represent nesting. For example, caddy.directive.subdir: value creates a sub-directive block.
    • Sites and Snippets:
      • caddy: example.com creates a site block.
      • caddy: (snippet_name) creates a snippet.
    • Global Options: Labels with no value (e.g., caddy.email: user@example.com) are treated as global options.
    • Named Matchers: Use the @ symbol to create named matchers (e.g., caddy.@match.path: /path /path/*).

    Ordering and Isolation

    • Alphabetical Order: By default, directives from labels are sorted alphabetically.
    • Isolation: Use a numeric suffix (e.g., caddy.route_0.a: value) to isolate directives into separate blocks (like multiple route blocks).
    • Custom Ordering: Use a numeric prefix (e.g., caddy.1_bbb: value) to define a specific execution order within a block.
  6. Build a custom Caddy image with the docker-proxy plugin

    master

    If you need additional Caddy plugins, use the builder variant of the official Caddy Docker image. You must override the CMD to use the caddy docker-proxy command. Use the module name github.com/lucaslorentz/caddy-docker-proxy/v2 during the build process.

    ARG CADDY_VERSION=2.6.1
    FROM caddy:${CADDY_VERSION}-builder AS builder
    
    RUN xcaddy build \
        --with github.com/lucaslorentz/caddy-docker-proxy/v2 \
        --with <additional-plugins>
    
    FROM caddy:${CADDY_VERSION}-alpine
    
    COPY --from=builder /usr/bin/caddy /usr/bin/caddy
    
    CMD ["caddy", "docker-proxy"]
  7. Configure Caddy Ingress Networks

    master

    Caddy-Docker-Proxy attempts to automatically detect which Docker networks are 'ingress' networks to find upstreams. If this detection fails, you can manually specify the networks using:

    1. Environment Variable: CADDY_INGRESS_NETWORKS (e.g., CADDY_INGRESS_NETWORKS=caddy).
    2. CLI Option: --ingress-networks.
    3. Per-Container Label: Add the label caddy_ingress_network with the name of the network to the specific container or service.
  8. Understand DockerLoader lifecycle and event monitoring

    master

    Once Start() is called, the DockerLoader performs the following:

    1. Environment Loading: Loads variables from the specified EnvFile.
    2. Client Setup: Establishes connections to all provided DockerSockets.
    3. Event Monitoring: Runs a background goroutine (monitorEvents) that listens for specific Docker events:
      • Containers: create, start, stop, die, destroy
      • Services: create, update, remove
      • Configs: create, remove
      • Networks: connect, disconnect
    4. Configuration Generation: When events occur (subject to EventThrottleInterval), it generates a new Caddyfile and converts it to JSON.
    5. Configuration Push: Automatically pushes the updated configuration to controlled servers (remote via Admin API or local via in-process loading).
  9. Inspect the generated Caddyfile for troubleshooting

    master

    Whenever the configuration changes, Caddy-Docker-Proxy automatically saves the generated Caddyfile to Caddyfile.autosave inside the Caddy config directory. You can inspect this file to debug label-to-Caddyfile conversion issues. The full path is logged at startup (typically /config/caddy/Caddyfile.autosave in Docker).

    docker exec <caddy-container> cat /config/caddy/Caddyfile.autosave
  10. Common Proxying Recipes with Labels

    master

    Use these label patterns for common reverse proxy scenarios:

    Proxy all requests to a domain:

    caddy: example.com
    caddy.reverse_proxy: "{{upstreams}}"

    Proxy to a specific subpath in the container:

    caddy: example.com
    caddy.rewrite: "* /target{path}"
    caddy.reverse_proxy: "{{upstreams}}"

    Proxy matching a path and stripping the prefix:

    caddy: example.com
    caddy.handle_path: /source/*
    caddy.handle_path.0_rewrite: "* /target{uri}"
    caddy.handle_path.1_reverse_proxy: "{{upstreams}}"

    Proxying multiple domains with individual certificates:

    caddy: example.com, example.org, www.example.com, www.example.org
    caddy.reverse_proxy: "{{upstreams}}"
  11. Use the `upstreams` Template Function

    master

    The {{upstreams}} function is used within Go templates in labels to automatically retrieve the IP addresses or DNS names of the Docker resource (service or container) connected to the Caddy ingress networks.

    Syntax: upstreams [protocol] [port]

    • protocol: http or https (optional).
    • port: The target port (optional).

    Important: When using upstreams in a YAML file, you must wrap the entire label value in double quotes to prevent YAML parsing errors.

    Examples:

    • {{upstreams}} $\rightarrow$ 192.168.0.1 192.168.0.2
    • {{upstreams https}} $\rightarrow$ https://192.168.0.1 https://192.168.0.2
    • {{upstreams 8080}} $\rightarrow$ 192.168.0.1:8080 192.168.0.2:8080
    • {{upstreams http 8080}} $\rightarrow$ http://192.168.0.1:8080 http://192.168.0.2:8080
    caddy.reverse_proxy: "{{upstreams 80}}"