RepoPilot

How does this find an available port? Show me the actual implementation.

About sindresorhus/get-port · 2 messages · last activity 7/20/2026

Question

How does this find an available port? Show me the actual implementation.

RepoPilot

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 to 0 (random) — matching the README's "will fall back to a random port" promise [readme.md:L49-L60].
  • Ports skipped via exclude are never returned.
  • Each candidate port is checked with getAvailablePort; if it's currently "locked" (recently handed out by a prior getPort() call within the 15-30s window, or explicitly reserved), it's rejected — unless the port is 0, in which case it just retries for another random port.
  • On success, the port is either permanently added to reservedPorts (if options.reserve was set) or added to a short-lived lockedPorts.young set 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 internal Locked error) 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-port

Public 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.