Skip to content

Agent Installation

The agent is a lightweight service to deploy on each server running a Caddy instance.

How reload works

When a vhost is updated, the agent writes the .caddyfile to SITES_DIR then reloads Caddy using one of two methods:

  1. Docker socket (preferred): if /var/run/docker.sock is accessible, the agent runs docker exec <caddy-container> caddy reload --config <CADDY_CONFIG_PATH>CADDY_CONFIG_PATH here is the path inside the Caddy container.
  2. Caddy API fallback: if the socket is not available, the agent POSTs the Caddyfile content to CADDY_API_URL/loadCADDY_CONFIG_PATH is then read by the agent itself (must be accessible via a volume mount).

Caddy running in Docker

The agent mounts the Docker socket to reload Caddy via docker exec. The sites directory is shared between both containers via a volume.

yaml
services:
  cadmin-agent:
    image: cletus/cadmin-agent:latest
    restart: unless-stopped
    environment:
      AGENT_TOKEN: ${AGENT_TOKEN}
      CADDY_API_URL: http://caddy:2019
      CADDY_LOG_FILE: /var/log/caddy/access.log
      SITES_DIR: /caddy/sites # path inside agent container
      CADDY_CONFIG_PATH: /etc/caddy/Caddyfile # path inside Caddy container
    ports:
      - '3002:3002'
    volumes:
      - /var/log/caddy:/var/log/caddy:ro
      - /opt/caddy/sites:/caddy/sites # host path → agent path
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - caddy

  caddy:
    image: caddy:2-alpine
    volumes:
      - /opt/caddy/sites:/etc/caddy/sites # same host path → Caddy path
      - /var/log/caddy:/var/log/caddy
    networks:
      - caddy

networks:
  caddy:
    external: true

TIP

The host directory (/opt/caddy/sites) is mounted into both containers under different paths. The agent writes files there, and CADDY_CONFIG_PATH is the path Caddy itself sees inside its own container.

Caddy running as a binary (systemd)

Without Docker, the agent uses the Caddy API fallback. The sites directory and Caddyfile must be readable/writable by the agent process.

yaml
services:
  cadmin-agent:
    image: cletus/cadmin-agent:latest
    restart: unless-stopped
    environment:
      AGENT_TOKEN: ${AGENT_TOKEN}
      CADDY_API_URL: http://localhost:2019
      CADDY_LOG_FILE: /var/log/caddy/access.log
      SITES_DIR: /etc/caddy/sites # same as on the host (volume 1:1)
      CADDY_CONFIG_PATH: /etc/caddy/Caddyfile # same as on the host (volume 1:1)
    ports:
      - '3002:3002'
    volumes:
      - /var/log/caddy:/var/log/caddy:ro
      - /etc/caddy/sites:/etc/caddy/sites # host path = container path
      - /etc/caddy/Caddyfile:/etc/caddy/Caddyfile
    network_mode: host # reach localhost:2019

WARNING

network_mode: host is required so the agent can reach the Caddy API on localhost:2019. The Docker socket is not needed in this mode.

Environment Variables

VariableDescriptionDefault
AGENT_TOKENBearer token for authentication (required)
CADDY_API_URLCaddy admin API URLhttp://caddy:2019
CADDY_LOG_FILEPath to Caddy access logs (inside container)/var/log/caddy/access.log
SITES_DIRDirectory for per-site .caddyfile files (inside agent container)/etc/caddy/sites
CADDY_CONFIG_PATHPath to the main Caddyfile — inside Caddy container when using Docker socket, inside agent container otherwise/etc/caddy/Caddyfile

Security

The agent exposes an HTTP port that allows reloading the Caddy configuration. Access must be restricted:

Recommendations

  • Firewall: only allow the hub's IP on the agent port (default 3002)
  • Token: use a long, unique token for each agent
  • Network: if possible, place the agent on a private network (VPN, internal network)

Verification

bash
curl http://localhost:3002/health

Expected response:

json
{ "status": "ok" }

The /health endpoint is the only one accessible without authentication.