Skip to content

devtunnel create routing flaps between near-equidistant clusters, silently creating cross-cluster duplicates #642

Open

Description

Summary

devtunnel create <id> chooses the cluster by nearest-by-latency. On a host roughly
equidistant between two clusters, the "nearest" pick is unstable between runs, so repeated
create of the same tunnel id lands it in different clusters on different runs —
producing two distinct tunnels that share one id (id is unique per cluster). Because the
public URL encodes the cluster (https://<id>-<port>.<cluster>.devtunnels.ms), the URL
also changes when the cluster flips, silently invalidating anything that persisted it
(webhooks, OIDC redirect URIs).

Repro

On a host where two clusters report near-identical ping (e.g. a box in Helsinki sees
uks1 and euw within a few ms — devtunnel clusters --ping):

devtunnel create my-tunnel        # run 1 -> my-tunnel.uks1
devtunnel delete my-tunnel.uks1
devtunnel create my-tunnel        # run 2, minutes later -> my-tunnel.euw   (nearest flipped)

Without an explicit delete, re-running create/host from tooling across the flip leaves
both my-tunnel.uks1 and my-tunnel.euw present.

Why it matters

  • Unstable public URL. A URL registered with an external service (Azure Bot Service
    messaging endpoint, OAuth redirect, webhook) breaks when the cluster flips, with nothing
    in the caller's config having changed.
  • Silent duplicates. Bare-name operations on a duplicated id then behave ambiguously —
    a host can attach to one cluster while a lookup resolves the other.
  • This is the root cause behind microsoft/aspire#14111,
    where .NET Aspire's DevTunnels integration (which shells bare-name create) inherits the
    flap.

Suggestions (any one would resolve it)

  • Make a tunnel id sticky to its cluster: once <id> exists in some cluster, a later
    bare create <id> reuses/targets that cluster instead of re-running nearest-routing.
  • Honour the persisted default cluster (devtunnel set / CurrentClusterId) on create.
  • Add hysteresis to nearest-cluster selection so a near-tie doesn't flip run to run.

(A documented create-time cluster override — --service-uri, see the companion docs issue
#643 — lets callers opt out, but the default behaviour is still the
trap.)

Environment

  • devtunnel CLI 1.0.1824+9e602bae78, Linux x64, Microsoft Entra ID auth.

Activity

  1. plequere-ms commented on Jun 9, 2026

    @plequere-ms
    Collaborator

    Thank you for reporting this. We have added this issue to our internal tracker for discussion and resolution.

  2. cpizzato commented on Aug 27, 2026

    @cpizzato

    I'm having a similar behavior and found this issue here. Not sure if it's relevant information but sharing to contribute.
    For me the duplication happens because the preferred cluster is often degraded. (see below)

    This is somehow related to an issue when using devtunnel on Aspire [#18790] (microsoft/aspire#18790)
    An attempt to overcome this was already merged giving a welcome possibility of setting a region by default #14112 but it's still missing the polyglot binding for typescript apphost [#19495] (microsoft/aspire#19495)

    https://global.rel.tunnels.api.visualstudio.com/api/v1/clusters/recommendations?preferredClusterId=euw
    
    {
      "preferredClusterId": "euw",
      "recommendedClusterId": "eun1",
      "isFallback": true,
      "recommendations": [
        {
          "clusterId": "eun1",
          "azureLocation": "NorthEurope",
          "azureGeo": "eu",
          "clusterUri": "https://eun1.rel.tunnels.api.visualstudio.com",
          "availability": "Available",
          "utilizationPercent": 21.49,
          "reason": "Available fallback cluster"
        },
        {
          "clusterId": "uks1",
          "azureLocation": "UkSouth",
          "azureGeo": "uk",
          "clusterUri": "https://uks1.rel.tunnels.api.visualstudio.com",
          "availability": "Available",
          "utilizationPercent": 60.55,
          "reason": "Available fallback cluster"
        },
        {
          "clusterId": "aue",
          "azureLocation": "AustraliaEast",
          "azureGeo": "au",
          "clusterUri": "https://aue.rel.tunnels.api.visualstudio.com",
          "availability": "Available",
          "utilizationPercent": 31.08,
          "reason": "Available fallback cluster"
        },
        {
          "clusterId": "euw",
          "azureLocation": "WestEurope",
          "azureGeo": "eu",
          "clusterUri": "https://euw.rel.tunnels.api.visualstudio.com",
          "availability": "Degraded",
          "utilizationPercent": 94.38,
          "reason": "Preferred cluster with degraded capacity"
        }
      ]
    }
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions