Architecture Overview
Portless uses a proxy-based architecture to route requests from stable.localhost URLs to your dev servers running on ephemeral ports.
Proxy Implementation
The proxy is built with Node.js built-in modules (http, http2, net) with no external dependencies for the core routing logic.
Request Routing
The proxy inspects theHost header to determine which backend to route to:
- Exact match:
myapp.localhostmatches a route registered formyapp.localhost - Wildcard match:
tenant1.myapp.localhostmatches a route registered formyapp.localhost - No match: Returns 404 with a list of active routes
HTTP/2 + HTTP/1.1 Support
When started with--https, the proxy creates an HTTP/2 secure server with HTTP/1.1 fallback:
Loop Detection
Portless detects forwarding loops (e.g., a Vite dev server proxying back through portless without rewriting theHost header):
Route Registration
Routes are stored in a JSON file at~/.portless/routes.json (or /tmp/portless/routes.json for privileged ports).
File Locking
Route updates are protected by a directory-based lock to prevent race conditions:Stale Route Cleanup
When loading routes, portless filters out stale entries whose owning process is no longer alive:Route Format
hostname: The full hostname (e.g.,myapp.localhost)port: The local port where the app is listeningpid: Process ID of the app (0 for static aliases)
Port Assignment
Portless assigns ports in the range 4000-4999 by default.Auto Port Discovery
Fixed Port Override
You can specify a fixed port instead of auto-assignment:Environment Variables
Portless injects environment variables into child processes:Framework Flag Injection
For frameworks that ignorePORT (Vite, Astro, React Router, Angular, Expo), portless auto-injects --port and --host flags:
Most frameworks respect
PORT automatically (Next.js, Express, Nuxt, Hono, Flask, FastAPI). Flag injection is only needed for a small set of frameworks.State Directory
Portless stores its state in a directory that depends on the proxy port:- Port >= 1024 (no sudo):
~/.portless/ - Port < 1024 (requires sudo):
/tmp/portless/