ecaddy Docs Overview

ecaddy — One global Caddy for all your local Rails projects

Instead of fighting port conflicts from multiple Caddy processes, ecaddy manages a single shared Caddy instance. Each project keeps its own Caddyfileecaddy copies it in and out of the global config on demand.

How It Works

One Caddy process. Many project fragments. Each project ships a Caddyfile; ecaddy copies it into ~/.config/caddy/sites/<name>.caddy on start and removes it on stop. The global Caddyfile imports everything in sites/*.caddy, so reloads are atomic and instant.

Browser
  │
  ▼
Caddy  (~/.config/caddy/Caddyfile)
  │         imports sites/*.caddy
  ├── fishme.localhost   → localhost:3104
  ├── letly.localhost    → localhost:3100
  └── traiderb.localhost → localhost:3106

Features

🌐

One Global Caddy

A single brew services-managed process serves every project. No per-project Caddy, no port collisions on 80/443.

🔌

Procfile-Native

Drop ecaddy run --config ./Caddyfile --site name into Procfile.dev. On Ctrl-C the fragment is removed and Caddy reloads cleanly.

🚨

Conflict Detection

Before registering a fragment, ecaddy parses it and BLOCKs on shared *.localhost domains or reverse_proxy localhost:PORT collisions.

🔐

HTTPS by Default

ecaddy setup runs caddy trust once, so https://*.localhost shows up green in browsers with no per-project cert juggling.

🔁

Idempotent Setup

ecaddy setup is safe to re-run. Every step (Homebrew install, scaffold, trust, brew-services start) checks before acting.

🩺

Built-in Doctor

ecaddy doctor audits every registered site for cross-site port and domain collisions, plus dead upstreams (app not running).

Quick Example

Write a project Caddyfile, point a Procfile line at it, and visit the URL:

# Caddyfile (in your Rails project root)

fishme.localhost {
  reverse_proxy localhost:3104
  tls internal

  log {
    output file log/caddy.log
  }
}
# Procfile.dev

web:   bin/rails server -p 3104
js:    yarn dev
caddy: ecaddy run --config ./Caddyfile --site fishme
bin/dev
# → visit https://fishme.localhost

Command Summary

CommandPurpose
ecaddy setupOne-time machine bootstrap (Caddy install, scaffold, trust, start)
ecaddy runRegister a fragment, block, unregister on shutdown (for Procfile)
ecaddy ensureRegister a fragment and exit (one-shot, persistent)
ecaddy listShow all registered sites
ecaddy up NAMEEnable a previously disabled site
ecaddy down NAMEDisable an enabled site without removing it
ecaddy edit NAMEEdit a site's fragment in $EDITOR, then validate + reload
ecaddy remove NAMEDelete a site's fragment and registry entry
ecaddy reloadValidate and reload the global Caddy config
ecaddy statusShow global Caddy state and per-site health
ecaddy doctorAudit all sites for port/domain conflicts and dead upstreams
ecaddy auditFull system + TLS audit with optional --fix prompts
ecaddy logs --site NAMETail the project's Caddy log

See the Commands reference for full flags and behaviour.

Why not just run Caddy per project?

Because ports 80 and 443 are global. Two Caddy processes can't both bind them. Multi-project local dev requires either: a per-project SOCKS-style port (ugly URLs), a shared reverse-proxy in front of N Caddies (extra hop), or a single shared Caddy with composed fragments (this approach).

Scope ecaddy targets macOS + Homebrew. Linux setups can use the same fragment-composition model but the ecaddy setup bootstrap (which calls brew services) is mac-specific.

Module Map

ModuleDescription
EasyCaddy::CLIThor CLI entry point; dispatches each subcommand to a dedicated command object
EasyCaddy::PathsSingle source of every filesystem path; honours ECADDY_HOME
EasyCaddy::RegistryReads/writes ecaddy.yml (name → { enabled, source_path })
EasyCaddy::SiteImmutable value object: name, enabled, source_path
EasyCaddy::ParserMinimal regex Caddyfile parser — extracts domains, ports, log paths
EasyCaddy::ConflictsDomain / port collision detection (check on register, doctor on demand)
EasyCaddy::CaddyWrapper around the caddy binary and brew services
EasyCaddy::Commands::*One class per CLI command (Setup, Run, Ensure, List, Up, Down, Doctor, …)