diff --git a/server/lib/traefik/getTraefikConfig.ts b/server/lib/traefik/getTraefikConfig.ts index b755ea341..6d8677211 100644 --- a/server/lib/traefik/getTraefikConfig.ts +++ b/server/lib/traefik/getTraefikConfig.ts @@ -597,6 +597,12 @@ export async function getTraefikConfig( // Public inference resources: same TLS/cert-resolver handling as // plain http-mode resources, but the service points at the AI // gateway instead of any real backend targets. + // + // A siteResource inference alias can share the exact same fullDomain + // as one of these (both ultimately proxy to the same aiGatewayUrl), + // so track which domains get a public router here and skip creating + // a second, duplicate router for the siteResource alias below. + const publicInferenceDomains = new Set(); for (const ir of inferenceResources) { if (!ir.enabled) continue; if (!ir.domainId || !ir.fullDomain) continue; @@ -644,6 +650,7 @@ export async function getTraefikConfig( }); Object.assign(config_output.http.routers, routers); Object.assign(config_output.http.services, services); + publicInferenceDomains.add(fullDomain); } // Private (siteResource) inference resources: routed by their alias @@ -670,6 +677,11 @@ export async function getTraefikConfig( for (const sr of siteResourcesInference) { if (!sr.enabled || !sr.fullDomain) continue; + // A public inference resource already owns a router for + // this exact fullDomain - both point at the same AI gateway, + // so avoid registering a duplicate router for it here. + if (publicInferenceDomains.has(sr.fullDomain)) continue; + if (!config_output.http.routers) config_output.http.routers = {}; if (!config_output.http.services) diff --git a/server/private/lib/traefik/getTraefikConfig.ts b/server/private/lib/traefik/getTraefikConfig.ts index 354a976b2..7bfc3460e 100644 --- a/server/private/lib/traefik/getTraefikConfig.ts +++ b/server/private/lib/traefik/getTraefikConfig.ts @@ -892,6 +892,12 @@ export async function getTraefikConfig( // Public inference resources: same TLS/cert-resolver handling as // plain http-mode resources, but the service points at the AI // gateway instead of any real backend targets. + // + // A siteResource inference alias can share the exact same fullDomain + // as one of these (both ultimately proxy to the same aiGatewayUrl), + // so track which domains get a public router here and skip creating + // a second, duplicate router for the siteResource alias below. + const publicInferenceDomains = new Set(); for (const ir of inferenceResources) { if (!ir.enabled) continue; if (!ir.domainId || !ir.fullDomain) continue; @@ -958,6 +964,7 @@ export async function getTraefikConfig( }); Object.assign(config_output.http.routers, routers); Object.assign(config_output.http.services, services); + publicInferenceDomains.add(fullDomain); } if (exitNode) { @@ -969,6 +976,11 @@ export async function getTraefikConfig( for (const sr of siteResourcesInference) { if (!sr.enabled || !sr.fullDomain) continue; + // A public inference resource already owns a router for + // this exact fullDomain - both point at the same AI gateway, + // so avoid registering a duplicate router for it here. + if (publicInferenceDomains.has(sr.fullDomain)) continue; + if (!config_output.http.routers) config_output.http.routers = {}; if (!config_output.http.services)