Portless: Vercel Labs Replaces localhost Ports With Named URLs
Every developer has a mental map of their ports. 3000 is the frontend. 3001 is the other frontend. 8080 is the API. 5432 is Postgres. 6379 is Redis. And at some point, you start a new project and something already owns 3000, so you change it to 3002, update three config files, and wonder why this is still a problem in 2026.
Portless from Vercel Labs replaces all of this with stable, named .localhost URLs. localhost:3000 becomes myapp.localhost. localhost:3001 for the API becomes api.myapp.localhost. Port numbers disappear.
3.8k stars. Actively maintained. Takes about 30 seconds to set up.
The Problem With Ports
Port-based local development has friction that’s so common we’ve stopped noticing it:
Port conflicts — two projects can’t share a port. You either kill one or change the port, which cascades into config changes everywhere.
Cookie and storage leaks — browsers scope cookies to localhost regardless of port. Run your auth app on :3000 and a different project on :3001 and they share cookies, localStorage, and IndexedDB. Data from one app bleeds into another, causing mysterious test failures.
No human-readable context — localhost:3000 in your browser history tells you nothing. Was that the Next.js app? The Vite project? The Express server? You’ve lost the context.
Monorepo and worktree pain — running multiple services simultaneously in a monorepo means juggling a mental port registry. Running the same project in two Git worktrees (common for comparing branches) requires changing ports, which changes configs, which breaks things.
Agents hardcode the wrong port — coding agents like Claude Code and Cursor read port numbers from config files and hardcode them into tests, scripts, and documentation. When the port changes, everything they wrote is wrong.
How Portless Works
npm install -g portless
# Instead of: next dev
portless run next dev
# → Your app is now at: myapp.localhost
# → Subdomains: api.myapp.localhost, docs.myapp.localhost
Portless runs a local DNS resolver and reverse proxy. When you start a dev server through portless run, it:
- Assigns a named
.localhostURL based on your project directory name - Routes that URL to whatever port the dev server actually picked
- Manages subdomains automatically for multi-service projects
The port still exists under the hood — Portless just makes it invisible.
Named URLs Fix More Than Conflicts
The .localhost naming changes more than just port conflicts:
Browser history becomes useful. myapp.localhost and otherap.localhost are distinct entries. You can tell what you were working on.
Cookie isolation by default. Each .localhost subdomain is a separate origin. Cookies, localStorage, and IndexedDB are scoped correctly. No more mysterious cross-project state contamination.
Sharing with teammates. “What port is that on?” disappears from Slack. “It’s at projectname.localhost” is the answer — same for everyone on the team.
Stable URLs for documentation. Write http://myapp.localhost/api/users in your README and it works for every developer on the team, regardless of what port the server happened to pick.
The Agent Angle
This is the part most developer tool posts miss. Coding agents — Claude Code, Cursor, GitHub Copilot Workspace — interact with local dev environments by reading config files, running commands, and making HTTP requests. Port numbers are a sharp edge for them.
When an agent scaffolds a new service, it reads the existing ports, picks an available one, and hardcodes it into tests, env files, and documentation. When something changes that port — a conflict, a config update, a teammate running something else — everything the agent wrote breaks.
With Portless, the agent writes http://myservice.localhost instead of http://localhost:3847. That URL is stable. It doesn’t change when the underlying port changes. The agent’s output stays correct.
The Portless README explicitly calls this out: “For humans and agents.” It’s not an afterthought.
Framework Support
Portless works with the major dev server frameworks:
- Next.js
- Vite
- Express
- Nuxt
- React Router
- Angular
- Expo
The portless run command wraps any npm run dev equivalent. If your framework starts a dev server, Portless can front it.
Setup
npm install -g portless
# Wrap your existing dev command
portless run npm run dev
portless run next dev
portless run vite
portless run node server.js
Your app gets a named URL immediately. For multi-service projects, subdomains are automatically available:
myapp.localhost → frontend (port 3000)
api.myapp.localhost → API server (port 8080)
docs.myapp.localhost → docs site (port 4000)
No config files. No DNS setup. It just works.
Repo: github.com/vercel-labs/portless
Why This Should Have Existed Years Ago
Port-based local development is one of those things that everyone tolerates because everyone else tolerates it. There’s no technical reason localhost:3000 is better than myapp.localhost — it’s just older.
Portless is a small quality-of-life fix with outsized impact: faster onboarding (new devs don’t have to learn the port map), fewer mysterious bugs (cookie isolation), and more reliable agent output (stable URLs instead of port numbers that drift).
3.8k stars in a short time suggests the developer community agrees. If you’re running multiple local projects — especially in a monorepo or with AI coding tools — it’s worth the 30-second install.