Skip to content

PublishAsDockerFile causes port mismatch in Kubernetes publish when resource endpoint is referenced by another resource #14588

@bbartels

Description

@bbartels

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Description

When an ExecutableResource uses PublishAsDockerFile() and another resource references its endpoint via WithReference(), the Kubernetes publisher generates inconsistent ports — the referencing resource gets a different port than the one assigned to the executable's service/deployment.

Reproduction

Single file (repro.cs):

#:sdk Aspire.AppHost.Sdk@13.1.0
#:package Aspire.Hosting.AppHost@13.1.0
#:package Aspire.Hosting.Kubernetes@13.1.0-preview.1.25616.3
#:property NoWarn=ASPIRECOMPUTE003
var builder = DistributedApplication.CreateBuilder(args);
var api = builder.AddExecutable("api", "echo", ".", "hello")
    .WithHttpEndpoint(env: "PORT")
    .PublishAsDockerFile();
builder.AddContainer("gateway", "nginx")
    .WithHttpEndpoint(targetPort: 8080)
    .WithReference(api.GetEndpoint("http"));
builder.AddContainerRegistry("registry", "localhost:5001");
builder.AddKubernetesEnvironment("k8s");
builder.Build().Run();

Run with:

dotnet run repro.cs -- publish --publisher kubernetes --output-path ./aspire-output

Observed behavior

The log output shows the resource being created twice:

Creating Kubernetes resource for api
Creating Kubernetes resource for api
Creating Kubernetes resource for gateway

The generated output has a port mismatch:

Location Port
values.yamlservices__api__http__0 http://api-service:8001
templates/api/service.yamlport 8000
templates/api/deployment.yamlcontainerPort 8000
The gateway will try to reach the api at port 8001, but the api service/deployment exposes port 8000.

Expected behavior

All generated references to the api's endpoint should use the same port.

Root cause

PublishAsDockerFile (ExecutableResourceBuilderExtensions.cs#L145-L148) removes the original ExecutableResource from the model and adds a new ExecutableContainerResource in its place. The replacement shares the original's Annotations collection, but they are different object instances.
However, other resources still hold EndpointReference objects that point to the original resource (the source comment at L143-L144 acknowledges these "dangling references").
During Kubernetes publish, KubernetesEnvironmentContext.CreateKubernetesResourceAsync uses a Dictionary<IResource, KubernetesResource> cache keyed by object identity. When the gateway's environment is processed, it encounters the EndpointReference pointing to the original resource → cache miss → creates KubernetesResource #1, allocates port 8001 via PortAllocator. Then the main loop processes the replacement ExecutableContainerResource → different object, another cache miss → creates KubernetesResource #2, allocates port 8000 from the same PortAllocator.
The DeploymentTargetAnnotation (which controls YAML output) points to KubernetesResource #2 (port 8000), but the gateway's env vars were resolved using KubernetesResource #1 (port 8001).
This affects AddUvicornApp, AddPythonApp, and any ExecutableResource using PublishAsDockerFile whose endpoint is referenced by another resource.

Expected Behavior

No response

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version info

No response

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions