Interactive terminal app for managing local development domains on macOS and Linux.
hostly lets you map friendly hostnames like myapp.local to local ports and
serve them over trusted HTTPS — without hand-editing /etc/hosts or writing
Caddy config. It wires together two things for each domain you register:
- An
/etc/hostsentry pointing the hostname at127.0.0.1. - A Caddy reverse-proxy route to your app's port, with a locally-trusted certificate issued by Caddy's internal CA.
The result: https://myapp.local resolves to your dev server with a green
padlock, while the underlying app keeps running on a plain localhost:PORT.
- Node.js >= 18
- Caddy installed and on your
PATH. hostly drives Caddy through its admin API athttp://localhost:2019. - macOS (launchd) or Linux (systemd
--user) for the optional boot daemon. sudoaccess — editing/etc/hostsand installing the CA into the system trust store require elevation. The interactive UI primessudoon launch.
npm install -g hostlyhostlyLaunches a full-screen terminal UI. Navigate with the arrow keys, ↵ to
select, esc to go back, and ctrl+c to quit. The main menu has four areas:
- Domains — add, edit, and remove domains. A domain is a host
(
myapp.local), a target port, and a scheme (http,https, orboth). Saving reconciles both/etc/hostsand Caddy. - Proxy & Caddy — view admin-API reachability and active reverse-proxy routes, and re-sync routes from the saved registry.
- Certificate — trust or untrust Caddy's local root CA in the system trust store so browsers accept your HTTPS domains.
- Daemon — install or remove a boot-time service that re-applies your domains after Caddy restarts (see below).
hostly syncRe-applies every saved domain to Caddy, then exits. Caddy's admin-API config is
not persisted across restarts, so routes are lost whenever Caddy
(re)starts. sync restores them. It does not touch /etc/hosts, which already
survives reboots.
This is the command the boot daemon runs. Install the daemon from the Daemon screen to have routes restored automatically at login/boot via launchd (macOS) or a systemd user unit (Linux).
- Registry — domains are stored as JSON in your platform config directory
(resolved via
env-paths, e.g.~/Library/Application Support/hostly/domains.jsonon macOS). This is the source of truth that both/etc/hostsand Caddy are reconciled against. - /etc/hosts — entries are written inside a managed
# Hostly Start/# Hostly Endblock, leaving the rest of the file untouched. Writes fall back tosudo cpwhen the process lacks permission. - Caddy — hostly loads a single
hostlyserver (:80/:443) into Caddy's config via the admin API: areverse_proxyroute per domain, automatic HTTP→HTTPS redirects forhttpsdomains, and aninternalTLS issuer for locally-trusted certificates.
Built with Ink (React for the terminal), Zustand for UI state, and meow for argument parsing. Uses pnpm.
pnpm install
pnpm build # compile TypeScript to dist/
pnpm dev # compile in watch mode
pnpm test # lint (xo) + run tests (ava)Run the local build directly with node dist/cli.js.
src/
cli.tsx CLI entry — argument parsing and `sync` command
app.tsx Root component, screen router
screens/ One component per UI screen (domains, proxy, certificate, daemon)
libs/ Core logic: caddy, hosts, domains, daemon, registry
components/ Shared UI (header, status line, key hints, forms)
stores/ Zustand app store
types/ Shared type definitions
website/ Next.js marketing site (deployed separately)
MIT
