Getting Started
From zero to https://fishme.localhost in five steps. The first two are once-per-machine; the rest are once-per-project.
Requirements
- macOS with Homebrew installed
- Ruby 3.2 or newer
- A foreman-style process manager (
foreman,overmind,hivemind, orbin/devin Rails 7+)
ecaddy setup installs Caddy via brew install and runs it under brew services. The rest of the gem (fragment composition, conflict detection, registry) is portable, but the bootstrap helper is mac-specific.
Step 1 — Install the gem
gem install easy_caddy
That installs the ecaddy executable on your $PATH. Verify it:
ecaddy version
# ecaddy 0.1.0
Step 2 — Bootstrap Caddy
ecaddy setup
This is a one-time machine bootstrap. Every step is idempotent — running ecaddy setup a second time is a no-op for anything already in place.
- Installs Caddy via Homebrew if not already present.
- Scaffolds
~/.config/caddy/{sites,disabled}/. - Writes the global
Caddyfile(withimport sites/*.caddy). - Symlinks it into
/opt/homebrew/etc/Caddyfilesobrew servicespicks it up. - Runs
caddy trustto install the local CA in your system keychain — makeshttps://*.localhostgreen in browsers. - Starts Caddy as a
brew servicesbackground service.
Step 3 — Write your project Caddyfile
Put a Caddyfile in your project root. Write it however you need — ecaddy treats it as read-only source. The only automatic transform on copy: relative output file log paths are rewritten to absolute paths, so Caddy (running as a background service detached from your project directory) can actually write the log files.
# Caddyfile (in your Rails project root)
fishme.localhost {
handle /vite-dev/* {
reverse_proxy localhost:3054
}
reverse_proxy localhost:3104
tls internal
log {
level INFO
output file log/caddy.log {
roll_size 2mb
roll_keep 5
roll_keep_for 48h
}
}
}
vite.fishme.localhost {
reverse_proxy localhost:3054
tls internal
}
Pick unique ports across your projects. A common pattern:
| Project | App port | Vite port |
|---|---|---|
| fishme | 3104 | 3054 |
| letly | 3100 | 3050 |
| traiderb | 3106 | 3056 |
ecaddy command that touches a project uses a site name — a short identifier you choose, like fishme. It determines the fragment filename (sites/fishme.caddy) and is how up, down, edit, and remove target the project. The name is not read from the Caddyfile — you supply it explicitly with --site fishme (short: -s fishme).
Step 4 — Add a Procfile line
# Procfile.dev
web: bin/rails server -p 3104
js: yarn dev
caddy: ecaddy run --config ./Caddyfile --site fishme
When foreman (or overmind) starts, ecaddy run copies your Caddyfile into the global config and reloads Caddy. When you press Ctrl-C, it removes the fragment and reloads again — the domain disappears cleanly. No orphan registrations.
Step 5 — Allow .localhost in Rails
# config/environments/development.rb
config.hosts << /.*\.localhost/
Without this, Rails will reject the request with "Blocked host: fishme.localhost".
Step 6 — Start your project
bin/dev
Visit https://fishme.localhost — done. The browser shows a green padlock because caddy trust seeded the local CA in step 2.
Repeat per project
For every additional project, only steps 3 – 6 apply. Re-running ecaddy setup is unnecessary.
Without a Procfile
If you don't want a foreground Caddy process attached to your dev loop, use the one-shot variant:
ecaddy ensure --config ./Caddyfile --site fishme
This copies the Caddyfile, reloads Caddy, and exits. The registration persists across restarts (Caddy is a system service). Use ecaddy down fishme to disable it later, or ecaddy remove fishme to delete it entirely.
Next steps
- Commands reference — full flags and behaviour for every subcommand
- Troubleshooting —
doctor,audit, conflict detection, filesystem layout, common issues