Getting Started
Portless replaces port numbers with stable, named .localhost URLs for local development. For humans and agents.
- "dev": "next dev" # http://localhost:3000
+ "dev": "portless myapp next dev" # https://myapp.localhost
Install
npm install -g portlessInstall globally. Do not add as a project dependency or run via npx.
Run your app
# Enable HTTPS (one-time setup, auto-generates certs)
portless proxy start --https
portless myapp next dev
# -> https://myapp.localhost
# Without --https, runs on port 1355
portless myapp next dev
# -> http://myapp.localhost:1355The proxy auto-starts when you run an app. A random port (4000--4999) is assigned via the PORT environment variable. Most frameworks (Next.js, Express, Nuxt, etc.) respect this automatically. For frameworks that ignore PORT (Vite, Astro, React Router, Angular), portless auto-injects --port and --host flags.
Use in package.json
{
"scripts": {
"dev": "portless myapp next dev"
}
}Subdomains
Organize services with subdomains:
portless api.myapp pnpm start
# -> http://api.myapp.localhost:1355
portless docs.myapp next dev
# -> http://docs.myapp.localhost:1355Git Worktrees
portless run automatically detects git worktrees. In a linked worktree, the branch name is prepended as a subdomain so each worktree gets its own URL without any config changes:
# Main worktree -- no prefix
portless run next dev # -> http://myapp.localhost:1355
# Linked worktree on branch "fix-ui"
portless run next dev # -> http://fix-ui.myapp.localhost:1355Put portless run in your package.json once and it works everywhere -- the main checkout uses the plain name, each worktree gets a unique subdomain. No collisions, no --force.
Use --name to override the inferred base name while keeping the worktree prefix:
portless run --name myapp next dev # -> http://fix-ui.myapp.localhost:1355Custom TLD
By default, portless uses .localhost which auto-resolves to 127.0.0.1 in most browsers. If you prefer a different TLD (e.g. .test), use --tld:
sudo portless proxy start --https --tld test
portless myapp next dev
# -> https://myapp.testThe proxy auto-syncs /etc/hosts for custom TLDs when started with sudo, so .test domains resolve correctly.
Recommended: .test (IANA-reserved, no collision risk). Avoid .local (conflicts with mDNS/Bonjour) and .dev (Google-owned, forces HTTPS via HSTS).
How it works
Portless runs a reverse proxy on port 1355. Each app registers a route mapping its hostname to an assigned port. Requests to http://<name>.localhost:1355 are proxied to the app.
Browser (myapp.localhost:1355) -> proxy (port 1355) -> App (random port)Requirements
- Node.js 20+
- macOS, Linux, or Windows