What
The App composition renders the Deployment and the EKSPodIdentity (→ PodIdentityAssociation) concurrently. Nothing orders them. If a pod is admitted before the association has propagated to the EKS Pod Identity webhook, that pod gets no AWS credentials — and because injection happens at admission, no amount of restarting fixes it. The pod crashloops forever against a correctly-configured IAM role.
Hit on xplane-image-gallery on the freshly rebuilt cluster (2026-08-02): 36 restarts, CrashLoopBackOff, dying on
{"level":"fatal","error":"Access Denied.","message":"Failed to connect to storage"}
Why it is easy to misdiagnose
Every piece of the IAM story looks healthy, so the investigation goes to AWS and finds nothing:
EKSPodIdentity/xplane-image-gallery-s3-pod-identity — Synced=True Ready=True
PodIdentityAssociation — Ready=True, bound to exactly ns=apps sa=xplane-image-gallery
- IAM policy — correctly scoped to the right bucket:
s3:ListBucket arn:aws:s3:::eu-west-3-ogenki-xplane-image-gallery
s3:GetObject/PutObject/DeleteObject .../*
Bucket MR — Ready=True, external name matches STORAGE_BUCKET exactly
- Region matches, endpoint matches
The tell is in the pod spec, not in AWS:
$ kubectl get pod -n apps <pod> -o json | jq '.spec.containers[0].env[] | select(.name|test("AWS_CONTAINER"))'
# (empty — nothing injected)
$ kubectl get pod -n apps <pod> -o json | jq '.spec.volumes[] | select(.name=="eks-pod-identity-token")'
# (empty)
A working pod-identity workload (runlore-0) has all three:
AWS_CONTAINER_CREDENTIALS_FULL_URI=http://169.254.170.23/v1/credentials
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE=/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token
volume: eks-pod-identity-token
The timing
| Event |
Timestamp |
PodIdentityAssociation object created |
07:35:51Z |
Pod …-xbmpv admitted |
07:35:49Z — 2s before the PIA object existed |
PIA Ready=True |
07:35:59Z |
Pod …-67lg9 admitted |
07:36:00Z — 1s after Ready, still inside webhook propagation lag |
Both replicas lost the race. Neither ever recovered, across 36 restarts and ~3 hours.
Ruled out
automountServiceAccountToken: false (set by the claim, and a reasonable suspect since the token is a projected SA volume) is not the cause. Controlled admission test with the same ServiceAccount in the same namespace:
| Pod |
automountServiceAccountToken |
Injected? |
epi-test-automount-false |
false |
✅ env + volume |
epi-test-automount-default |
unset |
✅ env + volume |
The webhook injects regardless. The variable that mattered was when the pod was admitted.
Workaround
Delete the pods once the association is live — they are re-admitted and the webhook injects correctly:
kubectl delete pod -n apps -l app.kubernetes.io/instance=xplane-image-gallery
Confirmed: both replicas came back 1/1 Running, the AWS env vars are present, the app logs Storage client initialized instead of the fatal, and the XR is Synced=True Ready=True.
Proposed fix
Latch Deployment rendering on PodIdentityAssociation readiness for any App that declares an EPI — the same option("params").ocds pattern the composition already uses to gate AIGatewayRoute rendering on Deployment readiness (SPEC-002 FR-002). No Deployment until the association reports Ready=True, so pods can never be admitted into the gap.
Worth considering alongside it: a guard so this fails loudly rather than silently. An App that declares an EPI but whose pods have no AWS_CONTAINER_CREDENTIALS_FULL_URI is always a bug — right now the only signal is an application-level Access Denied that points at IAM.
Impact
Any App claim combining a workload with an EKSPodIdentity, on first creation — so it reproduces on every cluster rebuild, and is invisible on steady-state clusters where the association long predates any pod. xplane-image-gallery is the instance that surfaced it; xplane-harbor's S3 path uses a static IAM user and is unaffected, and app-wizard does not need AWS credentials at runtime.
What
The
Appcomposition renders theDeploymentand theEKSPodIdentity(→PodIdentityAssociation) concurrently. Nothing orders them. If a pod is admitted before the association has propagated to the EKS Pod Identity webhook, that pod gets no AWS credentials — and because injection happens at admission, no amount of restarting fixes it. The pod crashloops forever against a correctly-configured IAM role.Hit on
xplane-image-galleryon the freshly rebuilt cluster (2026-08-02): 36 restarts,CrashLoopBackOff, dying onWhy it is easy to misdiagnose
Every piece of the IAM story looks healthy, so the investigation goes to AWS and finds nothing:
EKSPodIdentity/xplane-image-gallery-s3-pod-identity—Synced=True Ready=TruePodIdentityAssociation—Ready=True, bound to exactlyns=apps sa=xplane-image-galleryBucketMR —Ready=True, external name matchesSTORAGE_BUCKETexactlyThe tell is in the pod spec, not in AWS:
A working pod-identity workload (
runlore-0) has all three:The timing
PodIdentityAssociationobject created07:35:51Z…-xbmpvadmitted07:35:49Z— 2s before the PIA object existedReady=True07:35:59Z…-67lg9admitted07:36:00Z— 1s after Ready, still inside webhook propagation lagBoth replicas lost the race. Neither ever recovered, across 36 restarts and ~3 hours.
Ruled out
automountServiceAccountToken: false(set by the claim, and a reasonable suspect since the token is a projected SA volume) is not the cause. Controlled admission test with the same ServiceAccount in the same namespace:automountServiceAccountTokenepi-test-automount-falsefalseepi-test-automount-defaultThe webhook injects regardless. The variable that mattered was when the pod was admitted.
Workaround
Delete the pods once the association is live — they are re-admitted and the webhook injects correctly:
Confirmed: both replicas came back
1/1 Running, the AWS env vars are present, the app logsStorage client initializedinstead of the fatal, and the XR isSynced=True Ready=True.Proposed fix
Latch
Deploymentrendering onPodIdentityAssociationreadiness for any App that declares an EPI — the sameoption("params").ocdspattern the composition already uses to gateAIGatewayRouterendering onDeploymentreadiness (SPEC-002 FR-002). No Deployment until the association reportsReady=True, so pods can never be admitted into the gap.Worth considering alongside it: a guard so this fails loudly rather than silently. An App that declares an EPI but whose pods have no
AWS_CONTAINER_CREDENTIALS_FULL_URIis always a bug — right now the only signal is an application-levelAccess Deniedthat points at IAM.Impact
Any
Appclaim combining a workload with anEKSPodIdentity, on first creation — so it reproduces on every cluster rebuild, and is invisible on steady-state clusters where the association long predates any pod.xplane-image-galleryis the instance that surfaced it;xplane-harbor's S3 path uses a static IAM user and is unaffected, andapp-wizarddoes not need AWS credentials at runtime.