Skip to content

feat(sandbox): use the in-cluster sandbox URL when reachable - #110

Open
BDev-Koyeb wants to merge 3 commits into
mainfrom
bruno/sandbox-internal-url
Open

BDev-Koyeb wants to merge 3 commits into
mainfrom
bruno/sandbox-internal-url

Conversation

@BDev-Koyeb

Copy link
Copy Markdown
Contributor

Context

On self-hosted clusters (MAIS), sandboxes are used both from inside the cluster and from outside. The API now returns internal_url next to public_url in the sandbox metadata (https://github.com/mistralai/api.koyeb.com/pull/8797), so in-cluster clients don't have to leave the cluster and come back.

Implementation

select_sandbox_url() in control_plane.py, used by both places that read public_url:

  1. no internal_url (SaaS, older API, older sandboxes) → public_url, as today
  2. KOYEB_SANDBOX_URL=internal|public → forced choice
  3. the internal URL's host resolves → internal_url (it's a *.svc.cluster.local name, so it only resolves in the cluster; the lookup is cached per host)
  4. otherwise → public_url

SandboxMetadata models (sync + async), spec and docs are edited by hand to add the field: regenerating with the current generator rewrites ~1000 files.

Checks / QA

  • pytest koyeb/sandbox: 379 passed, including new TestSelectSandboxUrl cases

Comment on lines +89 to +108
def select_sandbox_url(sandbox_metadata: Any) -> Optional[str]:
"""Pick which sandbox gateway URL to call.

The API returns a public URL and, on self-hosted clusters, an in-cluster
one. The in-cluster URL is used when its host resolves (we run inside the
cluster); KOYEB_SANDBOX_URL=internal|public forces the choice.
"""
public_url = getattr(sandbox_metadata, "public_url", None)
internal_url = getattr(sandbox_metadata, "internal_url", None)
if not internal_url:
return public_url
mode = os.getenv(SANDBOX_URL_ENV, "").strip().lower()
if mode == "public":
return public_url
if mode == "internal" or not public_url:
return internal_url
host = urlparse(internal_url).hostname
if host and _resolves(host):
return internal_url
return public_url

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Most important part, I know there are a lot of ifs but it is pretty explicit I would say, but let me know if you want it differently

@BDev-Koyeb
BDev-Koyeb marked this pull request as ready for review November 4, 2026 15:24
Comment thread koyeb/sandbox/sandbox.py Outdated
if metadata and metadata.sandbox:
return metadata.sandbox.public_url, metadata.sandbox.routing_key
return (
select_sandbox_url(metadata.sandbox),

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.

Can we somehow cache this in Sandbox object? Once we resolved once, we know the URL and don't need to pay the price anymore

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is great comment btw 👍

On it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think it is actually already cached, the picked URL is stored on the Sandbox (self._sandbox_url) and only reset after a redeploy

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

def _get_sandbox_url(self) -> Optional[Tuple[str, Optional[str]]]:
"""
Internal method to get the sandbox URL for health checks and client initialization.
Caches the URL after first retrieval.
Returns:
str: the public url where to reach the sandbox
Optional[str]: the routing key to use to reach the sandbox, if needed
"""
if self._sandbox_url is None:
url_data = self._get_url_and_header_from_metadata()
if url_data:
self._sandbox_url = (f"{url_data[0]}/koyeb-sandbox", url_data[1])

this gets it onlly if empty, and it should be there at that time, cleared at redeploy

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Tho looking into all this made me push fix for get_domain() that I found

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.

3 participants