
Who portless is for#
Full-stack developers running multiple local services
When you run a frontend, API, and background worker simultaneously, portless gives each service a named address (api.myapp.localhost, docs.myapp.localhost) that does not change between sessions. You stop copying port numbers from terminal output, and bookmarks stop breaking when a service restarts on a different port.
Skip if:
If you run a single app at a time and a port number in the URL is not a friction point, portless adds setup overhead without a clear benefit.
Teams using git worktrees for parallel branches
portless automatically assigns a subdomain per worktree branch, so running the main checkout and a feature branch simultaneously produces two distinct HTTPS URLs with no manual configuration. The main worktree gets https://myapp.localhost and a linked worktree on fix-ui gets https://fix-ui.myapp.localhost.
Skip if:
If your team does not use git worktrees, or if developers work on one branch at a time, the worktree routing feature provides no value.
Developers needing HTTPS for local OAuth flows
Some OAuth providers (Google, Apple) reject http://localhost redirect URIs. portless serves all apps over HTTPS with a trusted local CA, so OAuth redirect URIs work in local development. With a custom TLD like dev.example.com, even strict providers that reject .localhost and .test URIs can be satisfied.
Skip if:
If your local development does not involve OAuth, secure cookies, or browser APIs restricted to secure origins, HTTPS on localhost adds no functional value.
Monorepo teams with multiple workspace packages
portless discovers workspace packages automatically from pnpm-workspace.yaml or the workspaces field in package.json, starts each package's dev script, and routes them to subdomains under a shared project name. A single portless command at the repo root replaces per-package port coordination.
Skip if:
Single-package repositories and non-JavaScript projects get no value from the monorepo workspace discovery feature, since it is specific to npm, yarn, pnpm, and bun workspaces.
The problem it solves#
Local development workflows accumulate port numbers. Port 3000 is the frontend, 8080 is the API, 3001 is the admin panel, and 5555 is whatever that background service started last week. When two services compete for the same port, startup fails or you hunt through logs to find which process to kill. When you switch projects, you copy port numbers from terminal output into browser tabs.
The HTTPS problem is separate and harder. Browsers restrict certain APIs to secure origins. Some OAuth providers reject http://localhost redirect URIs entirely. Cookies that require the Secure attribute do not work over plain HTTP. Setting up local HTTPS the right way means installing mkcert, configuring a reverse proxy, and maintaining certificate trust across team members' machines: work most teams skip and then debug later.
How it solves it#
Named .localhost HTTPS URLs
Every app gets a stable .localhost address instead of a raw port. HTTPS and HTTP/2 are enabled by default: portless generates a local CA, adds it to your system trust store, and binds port 443 on first run. No browser certificate warnings, no manual certificate setup required.
Framework auto-detection and port injection
portless assigns a random port (4000-4999) via the PORT environment variable. Frameworks that respect PORT (Next.js, Express, Nuxt) work immediately. For frameworks that ignore it (Vite, Astro, React Router, Angular, Expo, React Native), portless auto-injects the correct --port and --host flags, reaching through package.json script definitions.
Git worktree subdomain routing
In a linked git worktree, portless automatically prepends the branch name as a subdomain. The main checkout gets https://myapp.localhost; a worktree on branch fix-ui gets https://fix-ui.myapp.localhost. No configuration changes are needed between worktrees, and no URL collisions occur.
Monorepo workspace discovery
A single portless.json at the repo root covers all workspace packages. portless discovers packages from pnpm-workspace.yaml or the workspaces field in package.json (npm, yarn, bun). Running portless from the repo root starts all packages that have a dev script, each at its own subdomain.
OS startup service installation
Install the proxy as a launchd (macOS), systemd (Linux), or Task Scheduler (Windows) startup service so named URLs are available after every reboot. The service remembers its configuration: port, TLS mode, custom TLD, and LAN mode are all persisted and reused without repeating flags.
Tailscale and ngrok sharing integration
Add --tailscale to share a local app with teammates on your Tailscale network, or --funnel to expose it publicly via Tailscale Funnel. The --ngrok flag opens an ngrok tunnel alongside the local .localhost URL. Both integrations clean up their registrations automatically when the app exits.
Strengths and trade-offs#
Strengths
- No external traffic routing by defaultportless binds only to loopback addresses (127.0.0.1 and ::1) by default. No app traffic passes through any cloud service or external server. LAN mode requires an explicit --lan flag, and internet exposure requires explicitly enabling Tailscale Funnel or ngrok. Sensitive local data never leaves your machine without your intent.
- Zero manual certificate managementThe local CA is generated and trusted automatically on first run, including WSL setups where both the Linux trust store and the Windows Root store are updated. On Linux, certificate trust is handled for Debian, Arch, Fedora, and openSUSE. No mkcert commands, no nginx configuration, and no browser security exceptions to click through.
- Settings persist across restartsportless remembers port, TLS mode, custom TLD, and LAN settings from the most recent proxy run. A system restart or reboot does not silently revert to defaults. Explicit environment variables (PORTLESS_PORT, PORTLESS_HTTPS, PORTLESS_TLD) always take priority over persisted settings when you need to override.
- Apache 2.0 license with no usage restrictionsportless is Apache 2.0 licensed. You can use it in commercial projects, modify it, redistribute it, and run it in team environments without licensing fees or usage restrictions. The license does not require publishing modifications.
Trade-offs
- -Pre-1.0: state format may change between releasesportless is explicitly pre-1.0. The state directory format (~/.portless) may change between releases, which can require re-running portless trust after an update to re-establish certificate trust. Teams that install it per-project risk different contributors running different versions with incompatible state, which is why the README recommends the global install.
- -Requires Node.js 24 or laterThe minimum runtime is Node.js 24+. Projects on earlier Node.js versions cannot run portless without upgrading the runtime. This requirement is higher than many current CI baselines and older project setups, which may add friction for teams not already on Node 24.
- -Port 443 needs elevated privileges on macOS and LinuxBinding port 443 requires root on macOS and Linux. portless handles this with auto-elevation via sudo on first run, but it does require confirming a privilege prompt. In locked-down corporate environments or containers that restrict privilege escalation, you must use --no-tls or a higher port via --port.
portless vs alternatives#
portless vs ngrok
Both portless and ngrok give local development servers a stable URL, but they solve different problems. ngrok is a commercial tunneling service that exposes local apps to the public internet via ngrok's cloud infrastructure. portless is an open source local proxy that gives apps stable .localhost addresses without routing traffic through any external server.
| Feature | portless | ngrok |
|---|---|---|
| License | Apache 2.0 | Proprietary |
| Traffic routing | Local only (loopback) | Via ngrok cloud servers |
| Named local URLs | Yes (.localhost, .test, custom TLD) | Free tier: random subdomain |
| Persistent subdomains | Yes (local, always stable) | Paid tiers only |
| HTTPS | Auto-trusted local CA | ngrok-managed certificates |
| Internet exposure | Optional (--ngrok or --funnel flags) | Core feature |
| Git worktree routing | Yes (automatic) | No |
| Monorepo support | Yes | No |
portless is the better choice when you want stable local URLs without depending on an external service, need no account, and want no bandwidth limits. The local CA approach means HTTPS works in development including for OAuth flows that require a secure origin.
ngrok is still the better choice when you need to share a local app with someone outside your network, receive webhooks from external services in real time, or demo a work-in-progress to a client on a public URL. For those cases, portless integrates with ngrok via the --ngrok flag, so you can have both a stable local .localhost URL and an ngrok tunnel running at the same time.
portless vs manual local HTTPS setup
The alternative to portless without any external service is to configure mkcert, a reverse proxy (nginx or Caddy), and /etc/hosts entries by hand. This approach is free, but it requires installing and maintaining multiple tools, breaks when ports change, and needs to be repeated per project. portless replaces that entire setup with one npm install and one command per app.
Install and self-host#
Install portless globally via npm for the recommended setup.
```bash
npm install -g portless
```What it's built on#
- Languages
- TypeScript
- Frameworks
- Next.jsReact
FAQ#
Is portless free to use?
Yes. portless is Apache 2.0 licensed and free to use, modify, and redistribute for any purpose, including commercial projects. There is no hosted service, no paid tier, and no account required. You run it entirely on your machine.
Does portless work on Windows?
Yes. portless supports macOS, Linux, and Windows. On Windows, the local CA is added to the system trust store via certutil, and the OS startup service uses Task Scheduler. On WSL, portless updates both the Linux trust store and the Windows Root store so browsers on both sides trust the HTTPS certificates.
How does portless handle HTTPS certificates?
On first run, portless generates a local Certificate Authority (CA) and adds it to your system trust store. All apps served through the proxy use certificates signed by this CA, so no browser security warnings appear. You can supply your own certificates via --cert and --key flags if preferred, for example to use certificates generated by mkcert.
Does portless expose my app to the internet?
No. By default, portless binds only to localhost (127.0.0.1 and ::1), so no traffic leaves your machine. LAN mode (--lan) makes apps accessible on your local network via mDNS under .local addresses. Public internet exposure requires explicitly adding the --funnel flag for Tailscale Funnel or --ngrok for an ngrok tunnel.
Can I use portless in a CI environment?
portless is not designed for CI. In non-interactive environments (no TTY or CI=1), portless exits with a descriptive error rather than prompting, so task runners like turborepo and CI pipelines fail early with a clear message instead of hanging. Use portless in local development only.
Similar open-source tools#
invidious
Watch YouTube without ads, tracking, or a Google account
terminal-browser
Full Chromium browser rendering inside your terminal
kilocode
Open source AI coding agent. 500+ models at zero markup.
iroh
Connect devices seamlessly without relying on the cloud.
CLI-Anything
Empower AI agents with agent-native CLIs
RuView
Intelligent AI agents for real-world applications

