vercel-labs /portless
Replace port numbers with stable, named local URLs. For humans and agents.
★ 12,793⑂ 447👁 22▤ 47
12,793
stars
+0+9+152
447
forks
22
watchers
–
prs
–
issues
47
releases
–
discussions
README
portless Replace port numbers with stable, named .localhost URLs for local development. For humans and agents. diff - "dev": "next dev" http://localhost:3000 + "dev": "portless run next dev" https://myapp.localhost portless runs a local reverse proxy and gives each dev server a name. Your app listens on a free port that portless assigns, and you open https://.localhost instead of remembering which port it took. The URL stays the same across restarts, so bookmarks, cookies, OAuth redirects, and an agent's test script keep pointing at the right app. Install bash npm install -g portless To pin the version per project, install it as a dev dependency with npm install -D portless. portless is pre-1.0, so contributors may run different versions, and a change to the state directory format between releases can require running portless trust again. Quick start From a project with a "dev" script in package.json: bash portless - runs "dev", https://.localhost The name comes from package.json, the git root, or the directory. Pass a name and a command to choose both: bash portless myapp next dev - https://myapp.localhost - The proxy starts on the first run. It serves HTTPS with HTTP/2 on port 443, so the first run creates a local certificate authority, adds it to the system trust store, and may ask for sudo. Use --no-tls for plain HTTP, or --unprivileged to skip sudo (Unprivileged mode). - portless run infers the name and detects git worktrees. In a linked worktree, the last / segment of the branch becomes a subdomain, so fix-ui.myapp.localhost and myapp.localhost run side by side. Use --prefix or PORTLESS_PREFIX to set the label, or configure the label style and hostname template. - A name with dots is a subdomain: portless api.myapp pnpm start serves https://api.myapp.localhost. - PORTLESS=0 pnpm dev runs the command directly, without the proxy. The full walkthrough is in Getting started. How it works Each app gets a random port from 4000 to 4999 through the PORT environment variable, and registers a route from its hostname to that port. The proxy forwards https://.localhost to the route. Most frameworks read PORT. For those that don't, such as Vite, Astro, Angular, Expo, and Wrangler, portless adds the matching port and host flags to the command (Port assignment). Outside LAN mode, the proxy listens only on 127.0.0.1 and ::1. It keeps /etc/hosts in sync for route hostnames, which custom TLDs such as .test need. If a route hostname will not resolve, the command that registered it warns and points you to portless hosts sync. To reach an app from another device or share it, use LAN mode for phones on the same network, or Tailscale, ngrok, or a Cloudflare quick tunnel with --tailscale, --ngrok, or --cloudflare. Upstream aliases Use --upstream to route an alias to an HTTP or HTTPS service listening on loopback: bash portless alias api 8080 --upstream https portless alias api 8080 --upstream