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/27/2026
The core algorithm works in a few layers, all in index.js:
1. Low-level port check: checkAvailablePort
This is the fundamental primitive — it creates a net.Server, tries to bind (listen) with the given options, and resolves with whatever port the OS assigned (or confirmed available), then immediately closes the server:
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 an error event (e.g. EADDRINUSE), which rejects the promise instead.
2. getAvailablePort — checking across network interfaces
If a specific host was given, or the caller asked for a random port (port === 0), it just calls checkAvailablePort directly. Otherwise, it loops through all local host addresses (from OS network interfaces) and verifies the preferred port is free on each of them, tolerating EADDRNOTAVAIL/EINVAL (host not applicable to that interface) but rethrowing 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}); // eslint-disable-line no-await-in-loop
} catch (error) {
if (!['EADDRNOTAVAIL', 'EINVAL'].includes(error.code)) {
throw error;
}
}
}
return options.port;
};
[index.js:L61-L78]
3. getPorts (the default export, getPort) — orchestration
This is the main entry point. It:
- Normalizes
options.portinto an iterable list of candidate ports, and validates/builds theexcludeset [index.js:L89-L114]. - Sets up a periodic cleanup timer that ages out "locked" ports (ports returned recently that shouldn't be immediately re-handed-out) — moving
lockedPorts.youngintolockedPorts.oldand resettingyoung[index.js:L118-L129]. - Gets the list of local hosts to check against [index.js:L131].
- Iterates over the port candidates (via
portCheckSequence, which yields preferred ports first, then falls back to0for a random OS-assigned port), skipping excluded ports, callinggetAvailablePort, and re-trying withport: 0if the returned port is currently locked:
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-L163]
So in short: it tries each preferred port (checked as actually bindable via a real net.createServer().listen() call across relevant interfaces), skips any that are excluded or already locked/reserved by this process, falls back to asking the OS for a random free port (port: 0) if nothing preferred works, and throws if nothing at all could be found.
FOLLOW_UPS:
- What is
portCheckSequenceand how does it work? - How does the port-locking/reservation mechanism work internally?
- What is
getLocalHostsand why is it needed?
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.