[proposal] Placeholders: add exec, var

1) exec

Resolve a placeholder by running a command when the config (re)loads.

route /api* {
  reverse_proxy localhost:{exec: mise -C /srv/myapp x -- printenv APP_PORT}
}

Notes: opt‑in; short timeout; non‑zero exit fails the reload; value is cached into the active config.

2) var

Provide first‑class variables addressable as {var.NAME}, set via Caddyfile, JSON import, or the Admin API.

vars {
  import /etc/caddy/app-ports.json
  api_port 8039
}

route /api* {
  reverse_proxy localhost:{var.api_port}
}

/etc/caddy/app-ports.json:

{ "api_port": 8039, "billing_port": 9010 }

(Also allow setting vars via Admin API for tooling/CI.)

Why?

Coordinating ports is high‑friction:

  • The Admin API isn’t for everyone
  • Hard‑coding duplicates truth across systemd/compose/scripts and the Caddyfile; renumbering is tedious and error‑prone.
  • {env.*} and {file.*} push per‑app coordination onto Caddy (inject envs or create lots of tiny files).
  • Unix sockets avoids ports but isn’t always available (multi‑host, third‑party services, Windows, legacy).

The suggested placeholders solve the problem elegantly. I lean towards vars since I think implementation would be easier. I would use it by adding a pre-init hook to my app servers that sets/updates the JSON vars file with the current port.