[BUG] ALB Controller Helm chart injects a `/public` segment into the image path when a custom registry is used
- Dominant language
- TypeScript
- Stars
- 2.1k
- Forks
- 395
- Avg merge
- 4d 15h
- Merged PRs (30d)
- 14
Description
**Describe the bug**
The `alb-controller` Helm chart hardcodes a `/public` path segment into the computed image reference whenever `albController.image.registry` does not start with `mcr.microsoft.com`. This makes it impossible to pull the controller images from any registry other than MCR unless that registry happens to expose the images under a `public/` namespace.
The culprit is the `albController.imagePathPrefix` helper in `templates/_helper.tpl`:
```gotemplate
{{/* Image path prefix based on the registry */}}
{{- define "albController.imagePathPrefix" -}}
{{- if hasPrefix "mcr.microsoft.com" .Values.albController.image.registry -}}
{{- printf "%s" .Values.albController.image.registry -}}
{{- else -}}
{{- printf "%s/public" .Values.albController.image.registry -}}
{{- end -}}
{{- end -}}
```
`public/` looks like an internal MCR/ACR staging convention. It has no meaning in a customer registry, and the chart gives no way to opt out of it: neither `albController.image.registry` nor `albController.image.name.*` can express a final image reference without it, and `values.schema.json` sets `additionalProperties: false`, so no extra escape-hatch value can be passed either.
This also contradicts the [documented value contract](https://learn.microsoft.com/en-us/azure/application-gateway/for-containers/alb-controller-helm-chart#values), where `albController.image.registry` is described simply as "Container image registry for `alb-controller`", with no mention of an injected path segment.
Requesting label: `app-gateway-for-containers`.
**To Reproduce**
Steps to reproduce the behavior:
1. Pull the chart:
```bash
helm pull oci://mcr.microsoft.com/application-lb/charts/alb-controller --version 1.11.3 --untar
```
2. Render it against a custom registry, for example an Azure Container Registry fronting MCR through an [artifact cache rule](https://learn.microsoft.com/en-us/azure/container-registry/artifact-cache-overview):
```bash
helm template alb ./alb-controller \
--set albController.podIdentity.clientID=00000000-0000-0000-0000-000000000000 \
--set albController.image.registry=myregistry.azurecr.io/mcr.microsoft.com \
| grep 'image:'
```
3. Observed output (chart `1.11.3`, identical on `1.10.28`):
```text
image: "myregistry.azurecr.io/mcr.microsoft.com/public/application-lb/images/alb-controller-bootstrap:1.11.3"
image: "myregistry.azurecr.io/mcr.microsoft.com/public/application-lb/images/alb-controller-crds:1.11.3"
image: "myregistry.azurecr.io/mcr.microsoft.com/public/application-lb/images/alb-controller:1.11.3"
```
The repository `mcr.microsoft.com/public/application-lb/images/...` does not exist in the cache, so every pull fails with `ImagePullBackOff`.
With the default registry, the same render correctly produces `mcr.microsoft.com/application-lb/images/alb-controller:1.11.3`.
**Expected behavior**
The rendered image reference should be exactly the registry, the image name and the tag, with no segment the operator did not ask for:
```gotemplate
{{ .Values.albController.image.registry }}/{{ .Values.albController.image.name.controller }}:{{ include "albController.imageTag" . }}
```
which for the repro above gives:
```text
myregistry.azurecr.io/mcr.microsoft.com/application-lb/images/alb-controller:1.11.3
```
Concretely, `albController.imagePathPrefix` can collapse to:
```gotemplate
{{- define "albController.imagePathPrefix" -}}
{{- .Values.albController.image.registry -}}
{{- end -}}
```
The `mcr.microsoft.com` default in `values.yaml` is unchanged, so the default install renders exactly as it does today. Anyone who genuinely relies on the current behavior can restore it by appending `/public` to their own `albController.image.registry` value, which is one value change and is explicit rather than magic. If a hard break is not acceptable, an opt-out value (for example `albController.image.registryPathPrefix`, defaulting to `public` for non-MCR registries) would also unblock the scenario, though a verbatim registry is the behavior most Helm charts implement and the one the documentation already promises.
**Screenshots**
Not applicable.
**Environment (please complete the following information):**
- Chart version: `alb-controller` `1.11.3` (latest on `oci://mcr.microsoft.com/application-lb/charts/alb-controller` as of 2026-08-12), also reproduced on `1.10.28`
- Helm CLI version: `v4.2.3`
- Kubernetes version: not relevant, the defect is in chart templating and reproduces with `helm template` alone
- CLI Version: not applicable
- CLI Extension version: not applicable
- Browser: not applicable
**Additional context**
*Why this matters beyond "push the images yourself"*
We front all upstream registries with a single Premium ACR configured with [artifact cache rules](https://learn.microsoft.com/en-us/azure/container-registry/artifact-cache-overview), which is the Azure-recommended way to get private-network image pulls, rate-limit protection and availability-zone redundancy. Our MCR rule is registry-wide:
| Source repository | New ACR repository namespace |
| --------------------- | ---------------------------- |
| `mcr.microsoft.com/*` | `mcr.microsoft.com/*` |
Every MCR-hosted workload we run therefore just needs `registry: myregistry.azurecr.io/mcr.microsoft.com`. The ALB controller is the only chart that breaks under this scheme, because of the injected `/public`.
The obvious workaround, adding a bespoke cache rule from `mcr.microsoft.com/application-lb/images/*` to `public/application-lb/images/*`, is blocked by an ACR limitation documented on the same page: "Artifact cache rules can't overlap with other cache rules. In other words, if you have an artifact cache rule for a certain registry path, you can't add another cache rule that overlaps with it." Adding the `application-lb` rule would overlap with our `mcr.microsoft.com/*` rule, so accommodating this one chart means dismantling the registry-wide rule and enumerating every MCR repository we consume, one rule at a time, forever. That is a lot of permanent operational cost for a `printf` in a template.
*Prior report*
This was already reported in [#5159](https://github.com/Azure/AKS/issues/5159), where @JackStromberg [acknowledged the use case](https://github.com/Azure/AKS/issues/5159#issuecomment-3309924260) and stated the intent to remove the string in a future release:
> Although the chart change is trivial, there's some indirect work needed to complete this change. While the change won't be included in our upcoming release, we do intend to remove the string in a future release.
That issue was then auto-closed by `microsoft-github-policy-service[bot]` on 2025-10-27 without the change landing, and the `/public` segment is still present in `1.11.3` (verified today). Reopening [#5159](https://github.com/Azure/AKS/issues/5159) instead of tracking this one is perfectly fine by us; the goal is simply that the fix does not get lost to staleness a second time.
Contributor guide
Assessment
This issue has not been assessed yet.