| name | portless |
| compatibility | Claude Code 2.1.183+ |
| description | Named HTTPS .localhost URLs for local development with portless (v0.14.x). Eliminates port collisions, enables stable URLs for agents, integrates with emulate for API emulation aliases, git worktrees for branch-named subdomains, LAN mode (--lan) for mDNS .local hostnames reachable across devices, Tailscale sharing (--tailscale / --funnel), and OS startup-service install for boot persistence. Use when setting up local dev environments, configuring agent-accessible URLs, running multi-service dev setups, or testing from phones/tablets on the same wifi. Do NOT use for production deployments, CI environments (set PORTLESS=0), or DNS/hosting configuration. |
| tags | ["dev-server","localhost","https","portless","devops","mdns","lan"] |
| version | 1.2.0 |
| author | OrchestKit |
| user-invocable | false |
| complexity | low |
| context | inherit |
| persuasion-type | guidance |
| metadata | {"upstream-package":"portless","upstream-version-tested":"0.14.0"} |
Portless Integration
Named .localhost URLs for local development. Replaces localhost:3000 with https://myapp.localhost.
Full CLI reference: Load Read("${CLAUDE_SKILL_DIR}/references/upstream.md") for complete command docs.
New in 2026-04 → 2026-06 (portless 0.10.x → 0.14.0)
--ngrok flag (0.14.0) — share an app publicly via ngrok while local access keeps its .localhost URL. Pair with the existing Tailscale/Funnel options when you need a public URL without giving up the named-subdomain dev experience.
- Node.js 24+ required (0.13.1, BREAKING) — the proxy and CLI now require Node.js 24 or newer; older runtimes are unsupported. This release also hardens startup-service persistence so
.localhost URLs survive reboot reliably.
- State directory moved to
~/.portless (0.11, BREAKING) — state relocated from scattered/temp locations to ~/.portless; override with PORTLESS_STATE_DIR. Old state from pre-0.11 installs is not migrated automatically.
- OS startup service (0.13.0) —
portless service install / service status / service uninstall register a native startup service for the HTTPS proxy across macOS launchd, Linux systemd, and Windows Task Scheduler. .localhost URLs survive reboot without a manual portless proxy start. portless clean removes the service alongside CA + hosts cleanup.
- Tailscale readiness preflight (0.13.0) —
--tailscale and --funnel now validate Tailscale HTTPS + Funnel prerequisites before starting the child process, surfacing actionable errors instead of hanging during registration.
- Tailscale integration (0.12.0) —
--tailscale shares your app over your tailnet with automatic HTTPS on port 443; --funnel exposes it publicly via Tailscale Funnel. Apps receive PORTLESS_TAILSCALE_URL so they can reference their own public address. portless list now shows tailnet URLs.
- Zero-config mode (0.11.0) — bare
portless auto-discovers dev scripts from package.json. Multi-app monorepos get automatic subdomain assignment; Turborepo task-graph integration is wired in. portless.json config file supported. --script overrides the default "dev" script.
portless prune — removes orphaned dev servers and stale Tailscale registrations.
portless clean (extended) — now also tears down Tailscale registrations alongside CA + hosts cleanup.
- Rsbuild + VitePlus auto-port injection — same auto-wiring as Vite/Next.
- State directory moved to
~/.portless (was scattered).
- HTTPS on 443 by default (breaking from 0.9.x http:1355). Valid cert, no setup.
--no-tls reverts.
NODE_EXTRA_CA_CERTS auto-injected (0.10.2) into child processes — node HTTPS calls trust portless CA with zero setup.
--wildcard subdomains — https://*.myapp.localhost for multi-tenant / preview routing.
portless alias <name> <port> — map a docker-compose / emulate port to a named URL without a long-running run process.
portless clean — full teardown: stops proxy, removes CA, wipes state, cleans /etc/hosts.
--lan mode — mDNS .local hostnames reachable across wifi (phone, tablet, other machines) without router config.
- Fixed app ports —
--app-port 3000 / PORTLESS_APP_PORT for tools that need a known port (debuggers, docker).
- hosts-sync on by default for Safari compat (disable with
PORTLESS_SYNC_HOSTS=0).
- HTTP/2 HMR fixes for Vite/VitePlus/Next.js dev — websocket upgrades no longer break under h2.
- Expo / React Native support —
portless run expo start gives Metro a stable URL for device QR codes.
When to Use
- Starting a dev server that agents or browser tests will target
- Running multiple services locally (API + frontend + docs)
- Working in git worktrees (branch-named subdomains)
- Local OAuth flows (stable callback URLs)
- Connecting emulate API mocks to named URLs
Quick Start
portless run npm run dev
portless run --name api npm run dev:api
portless run --name web npm run dev:web
portless proxy start --lan
portless run npm run dev
portless clean
portless service install
portless service status
portless service uninstall
0.10.x breaking change: default switched from https://app.localhost to https://app.localhost on port 443. Use --no-tls to revert. NODE_EXTRA_CA_CERTS is injected into child processes automatically (0.10.2) — no manual cert setup. /etc/hosts is synced automatically for Safari; disable with PORTLESS_SYNC_HOSTS=0.
Framework-Specific Setup
Load Read("${CLAUDE_SKILL_DIR}/references/framework-integration.md") for full framework recipes.
Most frameworks (Next.js, Vite, Express) work with portless run <cmd>. Some need explicit flags:
| Framework | Auto-detected? | Extra flags needed |
|---|
| Next.js | Yes | None |
| Vite / Astro | Yes | None |
| Express / Fastify / Hono | Yes | None (reads PORT env var) |
| Ruby on Rails | Yes | None |
| FastAPI / uvicorn | No | --port $PORT --host $HOST |
| Django | No | $HOST:$PORT positional arg |
Why .localhost?
| Feature | .localhost (RFC 6761) | 127.0.0.1:PORT | /etc/hosts hack |
|---|
No /etc/hosts editing | Yes | Yes | No |
| HTTPS with valid cert | Yes | No | Manual |
| Wildcard subdomains | Yes | No | No |
| Works in all browsers | Yes | Yes | Varies |
| Cookie isolation per service | Yes | No | Yes |
| No port conflicts | Yes | No | Yes |
Key Environment Variables
When portless runs your command, it injects:
| Variable | Value | Use in agents |
|---|
PORT | Assigned ephemeral port (4000-4999) | Internal only |
HOST | 127.0.0.1 | Internal only |
PORTLESS_URL | https://myapp.localhost | Use this in agent prompts |
NODE_EXTRA_CA_CERTS | Path to portless CA (auto-injected 0.10.2) | Child node processes trust portless certs without setup |
Toggle env vars
| Variable | Effect |
|---|
PORTLESS=0 | Bypass portless entirely (CI) |
PORTLESS_SYNC_HOSTS=0 | Disable auto /etc/hosts sync (default: on in 0.10.1+) |
PORTLESS_STATE_DIR | Override state dir (default: ~/.portless or /tmp/portless for privileged ports) |
OrchestKit Integration Patterns
1. Agent-Accessible Dev Server
portless run npm run dev
agent-browser open $PORTLESS_URL
2. Emulate + Portless (Named API Mocks)
portless alias github-api 4001
portless alias vercel-api 4000
portless alias google-api 4002
3. Git Worktree Dev
portless run npm run dev
4. Bypass in CI
PORTLESS=0 npm run dev
Anti-Patterns
| Don't | Do Instead |
|---|
Hardcode localhost:3000 in tests | Use PORTLESS_URL or process.env.PORTLESS_URL |
| Run portless in CI | Set PORTLESS=0 in CI environments |
| Use numeric ports in AGENTS.md | Document the portless URL |
References
| File | Content |
|---|
references/upstream.md | Full portless CLI reference (synced from Vercel) |
references/upstream-oauth.md | OAuth callback patterns with stable URLs |
references/framework-integration.md | Framework recipes (FastAPI, Django, Docker, gotchas) |
checklists/new-project-setup.md | Step-by-step: add portless to a new project |