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 7/20/2026
Now I have the full picture. Here's the actual implementation, end to end:
1. The low-level bind check
checkAvailablePort tries to bind a net.Server to a port and immediately closes it:
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]
If the port is taken, listen emits 'error' (e.g. EADDRINUSE) and the promise rejects. If it succeeds, it resolves with the actual bound port (useful when port: 0 lets the OS pick one).
2. Checking across all local network interfaces
getAvailablePort decides whether to check just one host or all of them:
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]
If a specific host was given, or the port is 0 (random), it just checks that directly. Otherwise, it walks through every local address (from OS network interfaces) trying to bind the same preferred port on each, ignoring EADDRNOTAVAIL/EINVAL (address family mismatches) but propagating real conflicts.
3. The exported getPort() (called getPorts internally) orchestrates everything
export default async function getPorts(options) {
let ports;
let exclude = new Set();
if (options) {
if (options.port) {
ports = typeof options.port === 'number' ? [options.port] : options.port;
}
// ...validates and builds the `exclude` Set from options.exclude
}
const {reserve, ...netOptions} = options ?? {};
// sets up a periodic timeout to age-out locked ports (15-30s window)
const hosts = getLocalHosts();
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:L89-L168]
Key behaviors:
portCheckSequence(ports)presumably yields the preferred ports in order, then falls back to0(random) — matching the README's "will fall back to a random port" promise [readme.md:L49-L60].- Ports skipped via
excludeare never returned. - Each candidate port is checked with
getAvailablePort; if it's currently "locked" (recently handed out by a priorgetPort()call within the 15-30s window, or explicitlyreserved), it's rejected — unless the port is0, in which case it just retries for another random port. - On success, the port is either permanently added to
reservedPorts(ifoptions.reservewas set) or added to a short-livedlockedPorts.youngset that gets aged out by the periodic timeout, implementing the default 15-30 second lock window mentioned in the README/types [index.d.ts:L17-L30]. - Errors like
EADDRINUSE/EACCES(or the internalLockederror) just cause it to move on to the next candidate port in the sequence; anything else propagates. - If nothing in the sequence works, it throws
'No available ports found'.
So
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.