Skip to content

fix(nginx): Multi-domain upstream collides across deployments; server_names_hash not configurable #155

Description

@nfebe

Two bugs in the generated nginx vhosts surface when adding an extra domain to a deployment on a shared host. Both live in internal/nginx/manager.go / templates/infra/nginx/nginx.conf.

1. Additional-domain upstream uses the bare service name (routes to the wrong deployment)

The multi/additional-domain templates emit:

set $upstream {{.Service}}:{{.ContainerPort}};
proxy_pass {{.Protocol}}://$upstream;
resolver 127.0.0.11 valid=30s ipv6=off;

.Service is the Compose service name (commonly app), which is not unique across deployments. The shared proxy is attached to multiple project networks, so a service name like app resolves via the embedded DNS (127.0.0.11) to an arbitrary deployment's container. A custom domain then intermittently serves a different deployment on the box (whichever app the resolver returns), per request.

Reproduced: on a host running several stacks that each define a service named app, repeated requests to one deployment's domain return responses from different containers (for example different runtime versions in the response headers), confirming non-deterministic cross-deployment routing.

The primary-domain templates correctly use {{.DeploymentName}}:80 (unique). Only the multi/additional-domain path uses {{.Service}}.

Impact: cross-tenant request routing, non-deterministic, easy to miss because it sometimes hits the right container.

Proposed fix (configurable)

  • Resolve the additional-domain upstream to a deployment-scoped target. The agent already assigns a unique container_name per service (EnsureContainerNames); use that (or a per-deployment network alias) instead of the bare service name.
  • Surface the upstream target per domain in the UI/API. The Add Domain form lists only the service key (so duplicate keys like app across projects are indistinguishable) and Container Port, with no way to point at the unique container, so there is no workaround short of renaming service keys.

2. server_names_hash_bucket_size is a fixed value and not synced to existing installs

Adding a long hostname fails nginx -t with:

could not build server_names_hash, you should increase server_names_hash_bucket_size: 64

so the save rolls back. The base template (templates/infra/nginx/nginx.conf:24) sets 128, but:

  • existing installs still run the old value (the base http config is not re-applied on upgrade or on domain changes), and
  • 128 is a fixed ceiling that more or longer domains can still exceed.

Proposed fix (configurable)

  • Make server_names_hash_bucket_size (and server_names_hash_max_size) configurable, with a safe default, and/or auto-size from the count and longest length of configured domains.
  • Re-apply the base http config on agent upgrade and when adding domains.

Acceptance

  • Adding a second domain to a deployment routes only to that deployment's container, verified on a host running multiple stacks that share a service name.
  • Adding many long domains passes nginx -t and reloads on a freshly upgraded existing install, with no manual edits.

Files: internal/nginx/manager.go (multi-domain templates), templates/infra/nginx/nginx.conf.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions