LocalRun

Run every project at its own .test domain

LocalRun is a macOS menu-bar app. Point it at a project folder and that project becomes a named service at https://<name>.test, served by a Caddy instance LocalRun manages for you and proxied to whatever port your dev server is listening on. Start, stop, rebuild, tail logs, edit env vars, and keep a project alive across reboots, from the tray, without leaving a terminal open.

text
*.test  -->  DNS responder (127.0.0.1:15353)  -->  127.0.0.1

browser -->  Caddy :80/:443  -->  your project on 127.0.0.1:<port>

             daemon (DNS + Caddy + processes + IPC)  <-- Electron tray UI

What it does

  • Names your ports. checkout.test instead of "was that 3000 or 3001?". Three projects can run at once and none of them collide.
  • Real HTTPS. Caddy issues a certificate from its own local CA, and LocalRun trusts that CA in your login keychain. Green padlock, working Secure context, no --ignore-certificate-errors.
  • Finds the port for you. From .localrun.json, from a --port flag in your package.json scripts, from the port the process is already listening on, or from its startup output.
  • Runs the process, or doesn't. LocalRun can start your dev server and supervise it, or simply proxy to a server you started yourself in a terminal.
  • Keeps things up. Per project, choose whether it dies with the app, restarts on crash, or comes back after a reboot under launchd.
  • Talks to Claude. An MCP server ships in the app, so an agent can add a site, restart it, and read its logs. See MCP server.

How a request gets there

  1. macOS reads /etc/resolver/test and sends every *.test lookup to LocalRun's DNS responder on 127.0.0.1:15353, which answers 127.0.0.1 for any name under .test. Nothing in /etc/hosts changes, and no wildcard entry is possible there anyway.
  2. The browser connects to Caddy on :80/:443. Caddy matches the Host header against the sites LocalRun generated and reverse-proxies to 127.0.0.1:<port>.
  3. The daemon owns all of it: DNS, the Caddy config, the child processes, and the state in ~/.localrun. The tray app is only a client.

Scope

macOS 11 (Big Sur) or newer, Apple silicon only. There is no Intel, Linux or Windows build.

LocalRun is a local development tool. It binds loopback, its certificates are trusted only on your machine, and nothing it serves is reachable from the network. It is not a deployment target.

One seam

The only interface between the daemon and the UI is newline-delimited JSON-RPC 2.0 over a unix socket at $LOCALRUN_DIR/daemon.sock. The renderer never talks to the daemon directly, which is also why the MCP server can drive the same operations the tray does.

Next

Install gets the app running and .test resolving. If you would rather read the architecture first, go to How it works.