Lnch.sh

docs

How lnch works

lnch is a single Go binary. It finds the dev commands in a project, starts them in order, and can print a public link on lnch.sh.

What it is

One command sits between npm run dev and a full docker-compose or Tilt setup. The homepage at lnch.sh is the short version. This page is the rest.

lnch share registers a tunnel with https://lnch.sh and prints https://<id>.lnch.sh. The same tunnel also answers at https://lnch.sh/t/<id>/.

Install

From a checkout, with Go 1.23 or newer:

git clone https://github.com/lnch-sh/lnch
cd lnch
go install ./cmd/lnch

install.sh and install.ps1 install the latest GitHub release of lnch-sh/lnch.

curl -fsSL https://lnch.sh/install.sh | sh

Launch

cd my-app
lnch detect
lnch
lnch down

lnch with no arguments is lnch up. In a terminal it opens the dashboard. q quits and stops the stack.

lnch up --detach leaves it running. lnch status prints one snapshot and exits. lnch status --watch opens the dashboard without taking ownership of shutdown. lnch down is safe to repeat: a stopped project exits 0.

A second lnch up attaches to a stack that is already answering, instead of starting another copy.

Detect

The first match that claims the project wins. After that, lnch does not also start everything else it can see.

lnch.toml
That file. Detection stops.
Procfile
Each entry. Procfile.dev counts. Detection stops.
package.json
dev, then start, then serve. The lockfile picks pnpm, yarn, bun, or npm.
go.mod
go run ./cmd/<name> for each main, otherwise go run ..
Cargo.toml
cargo run for packages with a binary.
Python
Django runserver, uvicorn, or flask run.
Compose
docker compose up when the file has a services: key.
Make or just
dev, then start, and only when nothing else matched.

A root script that only runs turbo, nx, lerna, or a recursive package manager does not also start nested package.json scripts. Other languages in the tree are still detected.

lnch init writes lnch.toml from that detection. --stdout prints it. --force overwrites.

Share

lnch share

Prints https://<id>.lnch.sh and, in a terminal, a QR code. https://lnch.sh/t/<id>/ is the same tunnel. The CLI registers that name with the relay on https://lnch.sh, then long-polls it and forwards each request to the local server. A tunnel with no poll for three minutes is dropped.

--relay off stays on the LAN, for example http://192.168.1.20:43112/. Phones on the public URL use HTTP. They do not trust the local HTTPS certificate. WebSocket upgrades, including Vite hot reload, stay on the local URL.

lnch relay --addr 127.0.0.1:9090
lnch share --relay http://127.0.0.1:9090

That uses a relay you run. Its URL looks like http://127.0.0.1:9090/t/abc123/. On a host that receives *.example.com:

lnch relay --addr :8080 --domain example.com --scheme https

Relay choice is --relay, then share.relay in lnch.toml, then LNCH_RELAY, then https://lnch.sh. off disables it. If the relay is unreachable within five seconds, the LAN URL stays and the error is printed. LNCH_RELAY_TOKEN must match a self-hosted relay’s --register-token. The hosted relay does not require one.

Anyone with the link can reach the dev server.

Local HTTPS

HTTP services get an https://localhost proxy signed by a CA in ~/.lnch.

lnch trust

Windows adds the CA to the current user’s root store. macOS adds it to the login keychain. Linux prints the update-ca-certificates steps. Restart the browser afterward. --no-https skips the proxies. TCP services, including Compose, are left alone.

Dashboard

lnch up in a terminal, and lnch status --watch, use the same screen.

j / k
Select a service.
r
Restart it.
s
Share.
o, Enter
Open the selected URL.
g
Toggle the QR code.
/
Filter logs.
q
Quit. From lnch up, this also stops the stack.
X
Stop the stack from lnch status --watch.

NO_COLOR or TERM=dumb skips the QR. --no-tui streams prefixed logs. --json prints one JSON document, or {"error","hints"} on failure.

Config

examples/lnch.toml is a Postgres database, a Go API, and a Vite app. A service starts after each name in depends_on is healthy or running. ${port}, $PORT, and ${db.port} expand when that process starts.

name = "shop"

[share]
service = "web"
relay = "http://127.0.0.1:9090"

[[services]]
name = "api"
command = "go run ./cmd/api"
port = 8080
depends_on = ["db"]
health = "http://127.0.0.1:${port}/health"
restart = "on-failure"
env.DATABASE_URL = "postgres://127.0.0.1:${db.port}/shop?sslmode=disable"

protocol is http or tcp. restart is no or on-failure, up to five times. health is an HTTP URL, tcp://127.0.0.1:${port}, or none. primary is what lnch open and the share URL serve. public = true limits sharing to the marked services.

Runtime files live in ~/.lnch: runs, plans, logs, and the local CA. Each service receives PORT, LNCH=1, LNCH_SERVICE, and LNCH_ROOT.

Commands

lnch up
--dry-run prints the plan. --detach backgrounds it. --no-tui streams logs. --root picks the directory.
lnch share
Launch if needed, then print the LAN or relay URL and a QR code.
lnch status
One snapshot. --json for scripts. --watch for the dashboard.
lnch logs
lnch logs web --tail 50, or --follow.
lnch restart
lnch restart web restarts one service.
lnch open
Opens the primary local URL. --public opens the share URL.
lnch down
Stops the tree. A second run says it is already stopped.
lnch detect
Prints the plan and exits.
lnch init
Writes lnch.toml.
lnch trust
Installs the local CA.
lnch mcp
MCP on stdio for Cursor and other agents.
lnch relay
--addr, --domain, --scheme, --register-token.

Agents

Scripts and MCP stay on the detached JSON path. They should not sit in the dashboard.

lnch up --detach --json --no-tui
lnch status --json
lnch down --json
{"mcpServers":{"lnch":{"command":"lnch","args":["mcp"]}}}

Tools: lnch_detect, lnch_up, lnch_down, lnch_status, lnch_logs, lnch_restart, lnch_share. Pass relay to lnch_up when the stack is launched. lnch_share publishes a stack that is already running, using the relay from that launch.