Container pod fails to pull image from OCI Registry due to IAM policy misconfiguration (403 Access Denied)

Our container pods are failing to pull images from OCI Registry with 403 Access Denied errors. The deployment is completely blocked because pods can’t start without the images. I’ve set up a dynamic group for the container instances and created what I thought was the correct IAM policy, but it’s not working.

Error from pod events:


Failed to pull image "iad.ocir.io/namespace/myapp:v1.2.3"
Error: 403 Forbidden - insufficient authorization

My dynamic group matching rule:


All {instance.compartment.id = 'ocid1.compartment.oc1..aaaaaa...'}

And the IAM policy I created:


Allow dynamic-group container-instances to read repos in compartment MyCompartment

The pods are running in the MyCompartment compartment. What am I missing in the IAM configuration?

I updated the policy to use ‘manage repos’ but still getting 403 errors. The dynamic group name matches exactly what I see in the console. Could there be an issue with how the dynamic group matching rule is written? Do I need to match on something other than compartment ID?

Check the audit logs to see exactly what permission is being denied. The audit log will show the specific action that’s failing and what resource it’s trying to access. Run a query in OCI Audit for the past hour filtering on your container instances to see the denied requests. That will tell you if it’s a policy syntax issue or something else entirely.

I’ve implemented OCIR authentication for Container Engine multiple times. Here’s the complete solution addressing all the IAM components:

Dynamic Group OCID: Your dynamic group matching rule needs to correctly identify Container Engine worker nodes. The rule should match on the cluster or instance pool:


Any {instance.compartment.id = 'ocid1.compartment.oc1..xxxxx',
     resource.type = 'cluster'}

Or if you want to be more specific, match the exact cluster OCID:


Any {resource.id = 'ocid1.cluster.oc1.iad.xxxxx'}

Verify your dynamic group is correctly identifying instances by checking the “Matching Resources” section in the OCI Console.

IAM Policy Syntax: The policy needs the correct verb and resource type. For pulling images from OCIR, use:


Allow dynamic-group container-instances to read repos in tenancy

Or scope it to a specific compartment:


Allow dynamic-group container-instances to read repos in compartment MyCompartment where target.repo.name='myapp'

The key points:

  • Use read repos not read repositories - the resource type is `repos
  • If images are in the root compartment or a different compartment than your cluster, use `in tenancy
  • The verb read is sufficient for pulling images; manage is only needed for pushing

OCI Registry Permissions: OCIR requires authentication even with IAM policies. Container Engine automatically uses instance principal authentication, but you need to ensure:

  1. The dynamic group is created BEFORE deploying pods
  2. The policy grants access to the specific repository or all repos
  3. The image path in your deployment matches exactly: `.ocir.io//: Audit Log Review: Check what’s actually being denied:
oci audit event list --compartment-id <tenancy-ocid> \
  --start-time 2025-04-28T00:00:00Z \
  --end-time 2025-04-28T23:59:59Z \
  --query "data[?contains(response-payload, '403')]" \
  --all

Look for events with eventName like GetRepository or ReadRepository. The audit log will show:

  • The actual identity making the request (should be your dynamic group)
  • The exact resource being accessed
  • The specific permission that was denied

In your case, the issue is likely that your policy uses read repos in compartment but OCIR repositories might be in a different compartment or at the tenancy level. Change your policy to:


Allow dynamic-group container-instances to read repos in tenancy

This grants access to all repositories across all compartments. If you want to restrict access, use the where clause to limit by repository name.

After updating the policy, wait 1-2 minutes for it to propagate, then delete and recreate your pods to retry the image pull. The 403 errors should resolve once the dynamic group and policy are correctly configured.

For Container Engine instances, your dynamic group rule should match on the cluster OCID or the instance pool OCID, not just the compartment. Container instances have specific resource types. Try a matching rule like: `All {resource.type = ‘cluster’, resource.compartment.id = ‘ocid1.compartment…’}