Skip to content

[Serve] Bound the load balancer port search to the declared range - #10607

Open
roulbac wants to merge 3 commits into
skypilot-org:masterfrom
roulbac:fix/serve-lb-port-range
Open

[Serve] Bound the load balancer port search to the declared range#10607
roulbac wants to merge 3 commits into
skypilot-org:masterfrom
roulbac:fix/serve-lb-port-range

Conversation

@roulbac

@roulbac roulbac commented Aug 30, 2026

Copy link
Copy Markdown

The SkyServe controller task opens ports=['30001-30020'], but the
allocator on the controller called find_free_port(30001), which scans up
to 65535. Once 20 services were up, service 21 bound a port that was
never exposed: it reported READY and was unreachable.

serve.controller.load_balancer_port_range (default 30001-30020) is now
the single source for both the controller task's ports= and the port
search, so the two cannot drift. Exhausting the range raises and names
that config key instead of silently binding out of range. find_free_port
grows an end_port bound whose default preserves today's behavior for
every other caller.

A changed range only applies to a newly launched controller; existing
controllers keep the ports they opened at launch.

Tested (run the relevant ones):

  • Code formatting: install pre-commit (auto-check on commit) or bash format.sh
  • Any manual or new tests for this PR (please specify below)
  • All smoke tests: /smoke-test (CI) or pytest tests/test_smoke.py (local)
  • Relevant individual tests: /smoke-test -k test_name (CI) or pytest tests/test_smoke.py::test_name (local)
  • Backward compatibility: /quicktest-core (CI) or pytest tests/smoke_tests/test_backward_compat.py (local)

Devin Review

The SkyServe controller task opens ports=['30001-30020'], but the
allocator on the controller called find_free_port(30001), which scans up
to 65535. Once 20 services were up, service 21 bound a port that was
never exposed: it reported READY and was unreachable.

serve.controller.load_balancer_port_range (default 30001-30020) is now
the single source for both the controller task's ports= and the port
search, so the two cannot drift. Exhausting the range raises and names
that config key instead of silently binding out of range. find_free_port
grows an end_port bound whose default preserves today's behavior for
every other caller.

A changed range only applies to a newly launched controller; existing
controllers keep the ports they opened at launch.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

Devin Review

Comment thread sky/serve/server/impl.py Outdated
Comment thread sky/serve/service.py
- An admin policy that rewrites the range was still able to desynchronize
  the two ends this PR set out to keep in lockstep. impl.up read the range
  from the ambient pre-policy config while the controller is launched with
  the policy's mutated config, so a policy-set range would open one range
  and allocate from another. get_load_balancer_port_range now takes an
  optional config, and impl.up passes the mutated one it already uploads.

- Bounding the search regressed pool launches. _allocate_load_balancer_port
  runs before the branch that skips load balancer creation for pools, and
  impl.up does not open the range for pools at all, so an exhausted range
  refused a pool that needs no port. The bound now applies only to services
  that actually start a load balancer; for a pool the port stays the
  placeholder the service row requires.
The pool branch called find_free_port(start) without an end, so it
inherited the 65534 default cap. A configured range of 65535-65535
passes validation, and the search span for a pool then ran from 65535
to 65534: empty, raising a bare OSError before a single bind was tried.
That failed the launch on the one path the branch exists to exempt from
the range, and skipped the error message naming the config key.

Ask for the top of the port space explicitly. find_free_port keeps its
65534 default, so behavior is unchanged for every other caller, and the
default is now pinned by a test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant