#13845·vagrant

Port Forwarding doesn't work on MacOS 26.7+

Author: jcsalemCreated Sep 17, 2026Updated Sep 17, 2026
Labelswaiting-intake

Port forwarding doesn't work on MacOS 26.7+ and MacOS 27. It is due to a change in MacOS behavior that breaks is_port_open?.

Debug output

See Actual Behavior below

Expected behavior

This line should cause guest port 80 to be forwarded to host port 10080 since the host does not have that port open. Note that the problem occurs with any host port.

config.vm.network :forwarded_port, guest: 80, host: 10080

Actual behavior

The bug causes Vagrant to report an error similar to the following:

Vagrant cannot forward the specified ports on this VM, since they
would collide with some other application that is already listening
on these ports. The forwarded port to 10080 is already in use
on the host machine.

To fix this, modify your current project's Vagrantfile to use another
port. Example, where '1234' would be replaced by a unique host port:

  config.vm.network :forwarded_port, guest: 80, host: 1234

Reproduction information

Upgrade to MacOS Tahoe 26.7 or later. Or upgrade to MacOS Golden Gate.

Run vagrant up on a Vagrantfile with a line similar to:

config.vm.network :forwarded_port, guest: 80, host: 10080

Vagrant version

vagrant 2.4.9

Host operating system

MacOS 26.7 or later

Guest operating system

Doesn't matter but I was using Ubuntu 24.

Steps to reproduce

  1. Upgrade to MacOS Tahoe 26.7 or later. Or upgrade to MacOS Golden Gate.
  2. Run vagrant up on the Vagrantfile below:
config.vm.network :forwarded_port, guest: 80, host: 10080

Vagrantfile

Vagrant.configure("2") do |config|
  config.vm.box = "luminositylabsllc/bento-ubuntu-24.04"

  config.vm.define "test" do |s|
    s.vm.hostname = "test"
    s.vm.network "forwarded_port", guest: 80, host: 10080
  end
end

Solution

We have worked around it with the following patch by adding require_relative 'port_check_patch' to the top of our Vagrantfile. However, this should be fixed in Vagrant itself.

# port_check_patch.rb
# Workaround for a host-OS socket behaviour change, not a Vagrant or vagrant-parallels bug.
#
# macOS 26.7+ reports a refused non-blocking connect by making the socket writable and recording the
# error in SO_ERROR, rather than raising an error. Ruby's Socket.tcp treats writability as
# success and returns a dead socket, so Vagrant's is_port_open? reports every port as in use. 
#
# TCPSocket.new still reports refusals correctly, so use it instead, bounded by an explicit timeout.
# This only patches hosts that actually exhibit the bug: it is a no-op elsewhere and stops applying
# once macOS or Ruby fixes this. Delete this file and its require when that happens.
require 'socket'
require 'timeout'
require 'vagrant/util/is_port_open'

# True if Socket.tcp fails to raise on a port nothing is listening on.
def connect_timeout_reports_false_positive?
  probe = TCPServer.new('127.0.0.1', 0)
  free_port = probe.addr[1]
  probe.close
  Socket.tcp('127.0.0.1', free_port, connect_timeout: 0.1).close
  true
rescue StandardError
  false
end

if connect_timeout_reports_false_positive?
  module Vagrant
    module Util
      module IsPortOpen
        def is_port_open?(host, port)
          Timeout.timeout(1) { TCPSocket.new(host, port).close }
          true
        rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH,
               Errno::EACCES, Errno::ENOTCONN, Errno::EALREADY, Timeout::Error
          false
        end
        extend self
      end
    end
  end
end