← Back to Projects

Silent Failure Modes in Shared Helm Charts

Five ways a shared chart and deploy pipeline "succeeded" while the app was broken, and the fixes and checks that stop them

Platform: AKS
Tooling: Shared Helm chart + CI
Focus: Deployment reliability
Role: Platform engineer

Overview

Every team deploys to AKS through one shared Helm chart and one reusable pipeline. While onboarding a new service, I found five behaviours where the deploy reported success but the app was misconfigured, unreachable or not updated at all. I fixed the pipeline, documented the chart's real contract and added checks so other teams don't hit the same traps.

Architecture

App repoBuildimage + tagRegistryDeploy jobshared chart @ refhelm upgradeAKS objects
Shared deploy path. Each failure mode below happens at one of these stages.

The Five Failure Modes

#SymptomRoot causeFix
1Per-environment config never loadsThe chart ignores args and command, so the image CMD always runsOne config file with ${VAR} placeholders, filled from env vars
2Endpoints exist, but requests get no responseNamed targetPort, but the container port has no name, so the EndpointSlice port is unsetAlways use a numeric targetPort
3Deploy succeeds with no new podsThe mutable :main tag left the pod spec unchangedDefault the tag to the commit SHA
4Deploy fails on the namespaceThe deploy identity only had namespace-scoped rightsTerraform owns namespaces, and the pipeline skips creating them
5Frontend changes never shipThe build step only rebuilt the backend bundleRun both build steps, in order
diagnosing #2
# Is the Service actually bound to a port?
kubectl get endpointslice -l kubernetes.io/service-name=<svc> \
  -o jsonpath='{.items[*].ports}'
# Empty, or a port without a number = the targetPort name didn't resolve
values that work with the shared chart (illustrative)
deployment:
  containerPort: 7007
  env:
    - name: APP_BASE_URL
      value: https://app.dev.example.internal
service:
  targetPort: 7007          # a number, never a name
serviceAccount:
  annotations:
    azure.workload.identity/client-id: <client-id>

Guardrails I Added

Lessons Learned

Conclusion

None of these were hard bugs. They were invisible ones. Making them visible, and making the safe option the default, is what platform engineering is for.

Technologies Used

HelmKubernetesEndpointSlicesAzure Workload IdentityKey Vault CSI DriverGitHub ActionsDockerAzure Container Registry

About this case study: this write-up is drawn from my hands-on platform engineering experience. Organisation names, system names, figures and timelines have been changed or generalised, and all code, configuration and queries are illustrative examples written for this page.

← Inside-Out Ingress TroubleshootingHub-and-Spoke Landing Zone with Authoritative Private DNS →
← Back to Projects Get in Touch