RepoPilot

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

About sindresorhus/get-port · 2 messages · last activity 9/1/2026

Question

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

RepoPilot

The core implementation lives in index.js and works in a few layered steps:

1. Low-level port check via net.createServer

The base primitive is checkAvailablePort, which spins up a temporary TCP server on a given port/host, and if it successfully binds, reports back the port the OS assigned (or confirmed):

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 port 0 is passed to net.createServer().listen(), Node/the OS picks a free ephemeral port automatically — this is how "no preferred port" resolves to a random available port. The server is closed immediately after confirming the port is bindable, and unref() ensures it doesn't keep the process alive.

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}); // eslint-disable-line no-await-in-loop
		} 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 caller explicitly requested port 0 (any random port), it just does a single check.
  • Otherwise, it loops over every local host/interface address (gathered elsewhere via OS network interfaces, per the README description in [readme.md:L49-91]) and tries binding the same preferred port on each one, tolerating EADDRNOTAVAIL/EINVAL errors (which mean that address isn't valid for binding) but re-throwing anything else (like EADDRINUSE). If it survives all hosts, the original options.port is confirmed available and returned.

3. The main getPorts (default export) orchestration

This is where preferred ports, exclusions, locking/reservation, and fallback to random ports all come together:

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;
		}

		if (options.exclude) {
			const excludeIterable = options.exclude;

			if (typeof excludeIterable[Symbol.iterator] !== 'function') {
				throw new TypeError('The `exclude` option must be an iterable.');
			}

			for (const element of excludeIterable) {
				if (typeof element !== 'number') {
					throw new TypeError('Each item in the `exclude` option must be a number corresponding to the port you want excluded.');
				}

				if (!Number.isSafeInteger(element)) {
					throw new TypeError(`Number ${element} in the exclude option is not a safe integer and can't be used`);
				}
			}

			exclude = new Set(excludeIterable);
		}
	}

	const {reserve, ...netOptions} = options ?? {};
	...
	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 behavior:

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.