Concurrent port allocation for any application — avoid port conflicts in tests, servers, microservices, and development environments.
Library Composition: This tool uses file-based-semaphore-ts for atomic registry access, following PRINCIPLES.md Exception 2. Since all Tuulbelt tools have zero external dependencies, composing them preserves the zero-dep guarantee.
Running concurrent processes that need ports often causes "port already in use" errors:
Error: listen EADDRINUSE: address already in use :::3000
This happens in many scenarios:
- Parallel tests: Multiple test files try to use the same hardcoded ports
- Development environments: Multiple services started simultaneously
- Microservices: Services don't know which ports other services are using
- CI/CD pipelines: Concurrent builds running on the same machine
- Docker/containers: Random port selection can still collide under high parallelism
portres solves this by providing a centralized port registry:
- Each process requests a port from the registry
- The registry ensures ports are unique across all processes
- Ports are automatically released when processes exit
- Works across languages, frameworks, and applications
- Zero external dependencies — Uses only Node.js standard library + Tuulbelt tools
- File-based registry — Survives process restarts
- Stale entry cleanup — Automatically removes dead process entries
- Semaphore-protected registry — Atomic access via semats
- Result pattern — Clear error handling without exceptions
- CLI and library API — Use from shell or TypeScript
Clone the repository:
git clone https://github.com/tuulbelt/port-resolver.git
cd port-resolver
npm install # Installs dev dependencies + auto-fetches file-based-semaphore-ts from GitHubZero external dependencies — uses only Node.js standard library and file-based-semaphore-ts (a Tuulbelt tool with zero external deps).
CLI names — both short and long forms work:
- Short (recommended):
portres - Long:
port-resolver
Recommended setup — install globally for easy access:
npm link # Enable the 'portres' command globally
portres --helpFor local development without global install:
npx tsx src/index.ts --helpimport { PortResolver } from './src/index.ts';
const resolver = new PortResolver();
// Get a single port
const result = await resolver.get({ tag: 'my-test-server' });
if (result.ok) {
console.log(`Using port: ${result.value.port}`);
// Start your server on result.value.port
// Release when done
await resolver.release(result.value.port);
}
// Get multiple ports at once
const ports = await resolver.getMultiple(3);
if (ports.ok) {
console.log(`Got ports: ${ports.value.map(p => p.port).join(', ')}`);
}
// Release all ports at end of test suite
await resolver.releaseAll();Using short name (recommended after npm link):
# Get one available port
portres get
# 51234
# Get 3 ports at once
portres get -n 3
# 51234
# 51235
# 51236
# Get port with tag (for identification)
portres get -t my-server --json
# {"port":51234,"tag":"my-server"}
# Release a port
portres release 51234
# Release all ports for current process
portres release-all
# List all allocations
portres list
# Port PID Tag Timestamp
# 51234 12345 my-server 2025-12-29T01:00:00.000Z
# Show registry status
portres status
# Registry Status:
# Total entries: 1
# Active entries: 1
# Stale entries: 0
# Owned by this process: 1
# Port range: 49152-65535
# Clean stale entries (dead processes)
portres clean
# Clear entire registry
portres clearMain class for port allocation.
const resolver = new PortResolver({
minPort: 49152, // Minimum port (default: 49152)
maxPort: 65535, // Maximum port (default: 65535)
registryDir: '~/.portres', // Registry directory
allowPrivileged: false, // Allow ports < 1024
maxPortsPerRequest: 100, // Max ports per getMultiple()
maxRegistrySize: 1000, // Max total entries
staleTimeout: 3600000, // Stale entry timeout (1 hour)
});| Method | Description |
|---|---|
get(options?) |
Get a single available port |
getMultiple(count, options?) |
Get multiple ports at once |
release(port) |
Release a specific port |
releaseAll() |
Release all ports owned by this process |
list() |
List all port allocations |
clean() |
Remove stale entries |
status() |
Get registry status |
clear() |
Clear entire registry |
Check if a port is available by attempting to bind to it.
const available = await isPortAvailable(3000);
if (available) {
// Port 3000 is free to use
}See the examples/ directory for runnable examples:
npx tsx examples/basic.ts # Basic usage
npx tsx examples/advanced.ts # Advanced patternsnpm test # Run all tests (79 tests)
npm test -- --watch # Watch modeportres uses file-based-semaphore-ts (semats) as a library dependency for atomic registry access. This follows PRINCIPLES.md Exception 2 — Tuulbelt tools can compose other Tuulbelt tools since they all have zero external dependencies.
The semaphore ensures that concurrent port allocations from multiple processes never corrupt the registry file, even under high parallelism.
All methods return Result types:
type Result<T> =
| { ok: true; value: T }
| { ok: false; error: Error };Exit codes:
0— Success1— Error (invalid input, no ports available, etc.)
- Port range validation — Prevents allocation outside configured range
- Privileged port protection — Ports < 1024 require explicit
--allow-privileged - Path traversal prevention — Registry paths are validated
- Tag sanitization — Control characters removed from tags
- Registry size limits — Prevents resource exhaustion
- Secure file permissions — Registry files created with mode 0600/0700
This tool integrates with Tuulbelt's dogfooding strategy:
# Validate test reliability (local development)
./scripts/dogfood-flaky.sh 10
# Validate output determinism
./scripts/dogfood-diff.shSee DOGFOODING_STRATEGY.md for the full composition strategy.
▶ View interactive recording on asciinema.org
MIT — see LICENSE
See CONTRIBUTING.md for contribution guidelines.
Part of the Tuulbelt collection:
- File-Based Semaphore (TS) — Cross-platform process locking
- Test Flakiness Detector — Detect unreliable tests
- CLI Progress Reporting — Concurrent-safe progress updates
- More tools at tuulbelt.github.io
