dify/docker/caddy
Joey Yakimowich-Payne 6038fc25f5
Add multi-site Caddy helpers and document usage
- add startup/shutdown scripts that render a Caddyfile from JSON config and run health checks

- add Python utilities and a sample sites.json for declarative multi-site configuration

- document the workflow and ignore generated Caddy state artifacts

- normalize double-quote style across challenge workflow controllers, nodes, and tests
2025-10-15 22:03:56 -06:00
..
.gitignore Add multi-site Caddy helpers and document usage 2025-10-15 22:03:56 -06:00
__init__.py Add multi-site Caddy helpers and document usage 2025-10-15 22:03:56 -06:00
load_metadata.py Add multi-site Caddy helpers and document usage 2025-10-15 22:03:56 -06:00
README.md Add multi-site Caddy helpers and document usage 2025-10-15 22:03:56 -06:00
render_caddy.py Add multi-site Caddy helpers and document usage 2025-10-15 22:03:56 -06:00
sites.json Add multi-site Caddy helpers and document usage 2025-10-15 22:03:56 -06:00

Caddy Multi-Site Configuration

The Caddy helpers under docker/ can now generate a single Caddyfile that serves multiple services. Instead of hard-coding upstreams in the shell script, each site to be exposed is described declaratively inside docker/caddy/sites.json.

Layout

docker/
  startup-caddy.sh      # Generates Caddyfile + runs caddy start --config ...
  shutdown-caddy.sh     # Stops the running Caddy instance
  caddy/
    sites.json          # Default site definitions (Dify + HackAPrompt example)
    state/              # Generated Caddyfile, logs, pid files

Provide an alternative configuration file with --config or CADDY_SITE_CONFIG if you want a different set of sites.

# Use a staging configuration
./docker/startup-caddy.sh --config /path/to/staging-sites.json --regenerate

Config File Format

The configuration file is JSON with a top-level sites array. Each site object supports the keys below (all optional unless marked required):

Key Type Description
name string Display name used in logs; defaults to site.
address string required Caddy address such as example.com or :8080.
auto_https bool/string Disable auto HTTPS when serving plain HTTP (default: true).
https_redirect bool/string Emit an HTTP→HTTPS redirect block when a hostname is present.
acme_challenge bool/string Adds a handler for /.well-known/acme-challenge/*.
headers object Key/value pairs emitted inside a header { ... } block.
health_path string Path exposed as a simple respond handler (default /health).
health_check object { "url": "https://...", "skip": false } controls post-start checks.
app_url string Logged after startup for operator convenience.
log_file string Custom log path; defaults to $LOG_DIR/<name>-access.log.
api_routes array Each entry { "path": "/api/*", "upstream": "host:port" } creates a handle block with reverse_proxy.
static_routes array Each entry { "path": "/images/*", "root": "/var/www" }; add "browse": true to enable directory listing.
frontend object {"type":"reverse_proxy","upstream":"host:port"} or {"type":"static","root":"/dir","try_files":[...]} .
cache_static object { "paths": ["*.js", ...], "header": "public, max-age=..." }.

All string values are expanded with os.path.expandvars, which means you can reference environment variables—"${LOG_DIR}/dify-access.log", "${DIFY_API_UPSTREAM:-127.0.0.1:5001}", etc.

Example

{
  "sites": [
    {
      "name": "dify",
      "address": "${DIFY_ADDRESS:-:80}",
      "https_redirect": "${DIFY_REDIRECT:-false}",
      "api_routes": [
        { "path": "/api/*", "upstream": "${DIFY_API_UPSTREAM:-127.0.0.1:5001}" }
      ],
      "frontend": {
        "type": "reverse_proxy",
        "upstream": "${DIFY_FRONTEND_UPSTREAM:-127.0.0.1:3000}"
      },
      "health_check": {
        "url": "${DIFY_HEALTH_URL:-http://127.0.0.1/health}",
        "skip": "${DIFY_SKIP_HEALTHCHECK:-false}"
      },
      "app_url": "${DIFY_APP_URL:-http://127.0.0.1}"
    },
    {
      "name": "hackaprompt",
      "address": "${HACKAPROMPT_ADDRESS:-:8080}",
      "frontend": {
        "type": "static",
        "root": "${HACKAPROMPT_FRONTEND_ROOT:-/opt/hackaprompt-chat-viewer/frontend}",
        "try_files": ["{path}", "{path}/", "/index.html"]
      },
      "api_routes": [
        { "path": "/api/*", "upstream": "${HACKAPROMPT_API_UPSTREAM:-127.0.0.1:5002}" }
      ],
      "static_routes": [
        { "path": "/images/*", "root": "${HACKAPROMPT_APP_ROOT:-/opt/hackaprompt-chat-viewer}" }
      ]
    }
  ]
}

Health Checks & Logs

After Caddy starts, the script runs a curl-based health check for each site unless --skip-healthcheck is passed globally or the site entry sets "skip": true. Each check waits up to 30 seconds.

Logs are written to docker/caddy/state/logs/ by default. Adjust log_file per site if you want a different location.

Stopping Caddy

./docker/shutdown-caddy.sh

The shutdown helper remains unchanged; it simply reads the PID file and stops the running Caddy instance.