How does this find an available port? Show me the actual implementation.
How does this find an available port? Show me the actual implementation.
About sindresorhus/get-port · 2 messages · last activity 8/1/2026
The core logic lives in index.js and works in a few layers:
1. Low-level check: does a specific port/host bind successfully?
checkAvailablePort creates a temporary net.Server, tries to listen on the given options, and if it succeeds, immediately closes it and resolves with the bound port number. If listening fails (e.g. EADDRINUSE), the error event rejects the promise.
const checkAvailablePort = options =>
new Promise((resolve, reject) => {
const server = net.createServer();
server.unref();
server.on('error', reject);
server.listen(options, () => {
const {port} = server.address();
server.close(() => {
resolve(port);
});
});
});
[index.js:L47-L60]
2. Checking across all local hosts
getAvailablePort decides whether to check just one host (if options.host was explicitly given, or if requesting a random port with port: 0) or to loop through every local host/interface, calling checkAvailablePort for each. It tolerates EADDRNOTAVAIL/EINVAL errors (meaning that host doesn't support that address family) and rethrows anything else.
const getAvailablePort = async (options, hosts) => {
if (options.host || options.port === 0) {
return checkAvailablePort(options);
}
for (const host of hosts) {
try {
await checkAvailablePort({port: options.port, host});
} catch (error) {
if (!['EADDRNOTAVAIL', 'EINVAL'].includes(error.code)) {
throw error;
}
}
}
return options.port;
};
[index.js:L61-L78]
3. Top-level orchestration: getPorts (the default export, getPort)
This is where preferred ports, exclusions, locking, and reservation all come together:
- It normalizes
options.portinto an array (or uses the iterable as-is), and validates/collects theexcludeset [index.js:L89-L116]. - It sets up a periodic timer that rotates "young"/"old" locked-port sets so temporary locks expire after 15–30 seconds (unless
reserveis used) [index.js:L120-L131]. - It resolves the list of local hosts via
getLocalHosts()[index.js:L133]. - It iterates over a generated port-check sequence (
portCheckSequence(ports)— preferred ports first, then presumably random/range fallback), skipping excluded ports, callinggetAvailablePortfor each candidate, and re-trying with a new port (port: 0, i.e. random) if the returned port turns out to already be locked by this process:
for (const port of portCheckSequence(ports)) {
try {
if (exclude.has(port)) {
continue;
}
let availablePort = await getAvailablePort({...netOptions, port}, hosts);
while (isLockedPort(availablePort)) {
if (port !== 0) {
throw new Locked(port);
}
availablePort = await getAvailablePort({...netOptions, port}, hosts);
}
if (reserve) {
reservedPorts.add(availablePort);
} else {
lockedPorts.young.add(availablePort);
}
return availablePort;
} catch (error) {
if (!['EADDRINUSE', 'EACCES'].includes(error.code) && !(error instanceof Locked)) {
throw error;
}
}
}
throw new Error('No available ports found');
[index.js:L134-L162]
So the flow is: try each candidate port (starting with any preferred ones) → check it's actually bindable on the relevant host(s) via a real net.createServer().listen() call → make sure it isn't already locked/reserved by this same process → lock or reserve it → return it. If nothing works, it throws 'No available ports found' [index.js:L162].
Would you like me to look at portCheckSequence, getLocalHosts, or the locking/isLockedPort/Locked class definitions, since those weren't in the retrieved chunks?
FOLLOW_UPS:
- What does
portCheckSequencedo internally? - How is
getLocalHosts()implemented? - How does port locking/reservation actually work?
Want to ask your own question?
Open chat for sindresorhus/get-portPublic Q&A. Generated by RepoPilot from the actual source of sindresorhus/get-port. AI answers can be incomplete or stale — verify before relying on them.