hashicorp/nomad · error

reserved port collision %s=%d

Error message

reserved port collision %s=%d

What it means

AssignPorts could not allocate a requested reserved port because that port on the candidate host address is already reserved by another allocation on the same node. Nomad tracks used ports per IP via a bitset (getUsedPortsFor); when a requested static port collides and the port is not marked IgnoreCollision, the address is skipped and this error is returned. It indicates host port contention on a particular network interface.

Source

Thrown at nomad/structs/network.go:494

	for _, port := range ask.ReservedPorts {
		reservedIdx[port.HostNetwork] = append(reservedIdx[port.HostNetwork], port)

		// allocPort is set in the inner for loop if a port mapping can be created
		// if allocPort is still nil after the loop, the port wasn't available for reservation
		var allocPort *AllocatedPortMapping
		var addrErr error
		for _, addr := range idx.HostNetworks[port.HostNetwork] {
			// Guard against invalid port
			if port.Value < 0 || port.Value >= MaxValidPort {
				return nil, fmt.Errorf("invalid port %d (out of range)", port.Value)
			}

			// Check if in use
			if !port.IgnoreCollision {
				used := idx.getUsedPortsFor(addr.Address)
				if used != nil && used.Check(uint(port.Value)) {
					addrErr = fmt.Errorf("reserved port collision %s=%d", port.Label, port.Value)
					continue
				}
			}

			allocPort = &AllocatedPortMapping{
				Label:           port.Label,
				Value:           port.Value,
				To:              port.To,
				HostIP:          addr.Address,
				IgnoreCollision: port.IgnoreCollision,
			}
			break
		}

		if allocPort == nil {
			if addrErr != nil {
				return nil, addrErr
			}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Change the task's static reserved port to a free value or switch to a dynamic port (omit Value / use a port range).
  2. Move the task to a host_network with fewer allocations so the port is free on that interface.
  3. Set IgnoreCollision: true on the port if co-existence is safe in your environment.
  4. Spread static-port workloads across client nodes to reduce per-node contention.

Example fix

// before
ReservedPorts: [{Label: "http", Value: 8080}]
// after
ReservedPorts: [{Label: "http", Static: 0, To: 8080}] // dynamic host port mapped to container 8080
Defensive patterns

Strategy: validation

Validate before calling

// before submitting a job with static ports, check they're free on the target host
func validateStaticPortsFree(ports []int, host string) error {
  for _, p := range ports {
    if p < 1 || p > 65535 { return fmt.Errorf("port %d out of range", p) }
    ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, p))
    if err != nil { return fmt.Errorf("port %d in use", p) }
    ln.Close()
  }
  return nil
}

Type guard

func isValidPort(p int) bool { return p > 0 && p < 65536 }

Try / catch

null

Prevention

When it happens

Trigger: Calling NetworkIndex.AssignPorts with a NetworkResource whose ReservedPorts (or HostNetwork-reserved ports) include a port number already in the index's used set for that address; the port's IgnoreCollision flag is false.

Common situations: Two jobs on the same node request static port 8080; a service hardcodes well-known ports (80, 443) that the host already uses; re-scheduling after a stale allocation still holds the port in the index.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/0982f91760107384. Report an issue: GitHub.