Introduction

Over time, Oracle Cloud Infrastructure (OCI) Container Registry can grow very fast.
Every build creates new images, and many of them are never used again.

OCI already provides a feature called Global Image Retention Policy, but it works in a generic way and does not cover some common real-world needs. In this post, I describe a custom approach using an OCI Function to clean up container images in a safer and more flexible way.

Services demonstrated on this blog are:

Oracle Cloud Infrastructure Registry (also known as Container Registry) is an Oracle-managed registry that enables you to simplify your development to production workflow. Container Registry makes it easy for you as a developer to store, share, and manage container images (such as Docker images).

OCI Functions: OCI Functions is a fully managed, multi-tenant, highly scalable, on-demand, Functions-as-a-Service platform. It is built on enterprise-grade OCI and powered by the Fn Project open source engine. Use OCI Functions (sometimes abbreviated as Functions, and formerly known as Oracle Functions) when you want to focus on writing code to meet business needs.

Oracle Cloud Infrastructure Kubernetes Engine (OKE): is a fully-managed, scalable, and highly available service for deploying containerized applications to the cloud. With OKE, you can build, deploy, and manage cloud-native applications using open source Kubernetes that is certified as conformant by the Cloud Native Computing Foundation (CNCF).

Note:

  • This tutorial is designed solely for educational and study purposes. It provides an environment for learners to experiment and gain practical experience in a controlled setting. It is crucial to note that the security configurations and practices employed in this tutorial might not be suitable for real-world scenarios.
  • Security considerations for real-world applications are often far more complex and dynamic. Therefore, before implementing any of the techniques or configurations demonstrated here in a production environment, it is essential to conduct a comprehensive security assessment and review. This review should encompass all aspects of security, including access control, encryption, monitoring, and compliance, to ensure that the system aligns with the organization’s security policies and standards.
  • Security should always be a top priority when transitioning from a lab environment to a real-world deployment.

Objectives

The goal of this solution is to:

  • Keep only the last “n” images in each container repository
  • Never delete images that are currently in use by any container running in an OKE cluster
  • Avoid breaking running workloads or future pod restarts
  • Give more control than the native retention policy

This approach is useful for teams that:

  • Build images frequently
  • Use Git commit–based tags
  • Run multiple OKE clusters
  • Want cleanup automation without risk

The result is a safe, controlled, and predictable image purge process, fully managed by an OCI Function.

Prerequisites (mandatory)

  • Oracle account with admin level access permissions.
  • A compartment to create your resources. (take note the comparment name and ID)
  • VCN with a private subnet. For more information, see Creating a Virtual Cloud Network.
  • A previsouly created object storage bucket, see Creating an Object Storage Bucket.
  • Auth token to Enable Login to Oracle Cloud Infrastructure Registry, see Generating an Auth Token.
  • A previsouly created Oracle Kubernetes Engine (OKE) cluster with some applications deployed on it, see Quick Cluster Creation.
  • A cluster-admin access to the OKE cluster to create the RBAC configuration for the function.

Task 1: Set up Dynamic Groups

Go to your domain, click Dynamic Groups and create the following group.

  • Group Name: MyFunctions.
# Rule 1
ALL {resource.type = 'fnfunc', resource.compartment.id = 'pasteYourCompartmentOCID'}
# Rule 2
ALL {resource.type='resourceschedule', resource.compartment.id = 'pasteYourCompartmentOCID'}

Task 2: Create Policies

  1. Go to Policies and create the following policies.
  • Policy Name: FunctionsPolicies.
Allow dynamic-group 'Default'/'MyFunctions' to manage repos in compartment YOUR-COMPARTMENT-NAME
Allow dynamic-group 'Default'/'MyFunctions' to read app-catalog-listing in compartment YOUR-COMPARTMENT-NAME
Allow dynamic-group 'Default'/'MyFunctions' to use volume-family in compartment YOUR-COMPARTMENT-NAME
Allow dynamic-group 'Default'/'MyFunctions' to use virtual-network-family in compartment YOUR-COMPARTMENT-NAME
Allow dynamic-group 'Default'/'MyFunctions' to manage objects in compartment YOUR-COMPARTMENT-NAME where any {request.permission='OBJECT_CREATE', request.permission='OBJECT_INSPECT'}
allow dynamic-group 'Default'/'MyFunctions' to manage functions-family in compartment YOUR-COMPARTMENT-NAME

Task 3: Create OCI Container Registry

  1. Go to Developer Services, click Container registry and create a private repository for the Fn image.
  • Name: lab/fn-oci-image-purge.

2. Check the repositories and take a note of the Namespace.

3. Open the cloud shell, and proceed with the login on the registry. Check what is the correct URL for your region. In this tutorial, we are using Brazil East (Sao Paulo) where the registry URL is gru.ocir.io.

docker login gru.ocir.io
Username: <your container namespace>/youruser
Password: YOUR_AUTH_TOKEN_CREATED_EARLIER

Task 4: Create your read-only user on the OKE Cluster (require OKE cluster admin privileges)

To enable the function to list the container images currently running in the OKE cluster, it must be configured with a kubeconfig that grants read-only access to the cluster.
To achieve this, we will:

  • Clone the source code repository and get all needed files.
    In your cloud shell, run this:
git clone git@github.com:jctarla/fn-oci-image-purge.git
cd fn-oci-image-purge/
ls -lrt 

Now you’ve downloaded the code, we must modify the sample_configuration.json file with your captured OCIDS.
The sample_configuration.json:

{
    "config": {
        "OCI_COMPARTMENT_ID": "ocid1.compartment.oc1..xxxxx", ## <= change with your OCID Compartment ID
        "OCI_CLUSTER_ID": "ocid1.cluster.oc1.sa-saopaulo-1.xxxx", ## <= change with your OCID OKE Cluster ID
        "OCI_REPOSITORY_NAME": "app/my-application-login, app/my-application-backend, app/my-service", ## <= Include your repositories, you can use more than one.
        "RETENTION_COUNT": "5", ## <= The number of images to retain based on creation timestamp
        "OCI_REPORT_PREFIX": "", ## <= If you need a directory or prefix for your object storage
        "OCI_REPORT_BUCKET": "image-purge-report", ## <= The name of your created bucket
        "OCI_REPORT_NAMESPACE": "xxxx", ## <= Your namespace 
        "DRY_RUN": "True"  ##<= If True, nothing will be deleted, it will only generate the simulation. 
    }
}    

Edit the file and include your values here:

 cd src
 ls -lrt 
 ### edit this file and fill with your values
 vim sample_configuration.json 
  • Create a dedicated RBAC user with read-only permissions.

In your cloud shell where you have kubectl command ready and apply the file rbac_user.yaml, this file will create a service account on your OKE cluster called pod-reader-user

# make sure you're on the src directory of your functions code.
pwd
cd src
ls -lrt 
kubectl apply -f rbac_user.yaml
  • Generate a corresponding kubeconfig file associated with this user.
## Check if your kubectl is working...
kubectl get nodes
source ./generate_kubeconfig.sh
ls -lrt

Note Make sure you have kubeconfig file generated, this file will be packaged with the function.

Task 5: Creating the function and needed configurations.

Note Make sure you select your private subnet.

  1. Go to Developer Services, under Functions, click Applications and then click Create application.

We created a new application called: default-apps

2. Go to your cloud shell to download code and setup your function configuration.

3. Go to the cloud shell where you have Docker, OCI CLI, Fn Project CLI installed and run the following commands to initialize the function.

Note: If you followed the tasks, your Docker log in command has already been executed by now, if not, proceed with the Docker log in steps in Task 3.

fn create context oci-cloud --provider oracle-cs
fn use context oci-cloud
fn update context oracle.compartment-id PASTE_YOUR_COMPARTMENT_OCID
fn update context api-url https://functions.sa-saopaulo-1.oraclecloud.com
fn update context registry gru.ocir.io/PASTE_YOUR_REGISTRY_NAMESPACE/lab
fn list apps

Note: In this tutorial, we are using Brazil East(Sao Paulo) region, if you are using a different region, you need to change the api-url and registry locations.

Note the created application is listed!

4. Build & Deploy the function

pwd ## check if you're on the src directory
ls -lrt
fn deploy --app default-apps

5. Apply function configurations We need to capture the function OCID and apply the sample_configuration.json file that was created earlier.

## Run to identify the function OCID
fn list functions default-apps

oci fn function update --force \
  --function-id REPLACE_WITH_YOUR_FUNCTION_OCID \
  --from-json file://sample_configuration.json

Note In this initial configuration, the function uses DRY_RUN = True.
This means no images are deleted. Instead, the function only simulates the cleanup, shows which images would be deleted, and generates a report.
After you test the function and feel confident, you can change this value to DRY_RUN = False to enable the real delete process.
To change the value, update the sample_configuration.json file and run the oci fn function update command again.
You can repeat this as many times as needed.
You can also change this setting in the OCI Console by opening the function, then clicking on Configurations.

6. Invoke the function and verify if everything is running as expected.

fn invoke default-apps fn-oci-image-purge

Task 6: Checking execution report

  1. Go to Storage, under Object Storage & Archive Storage, select on Buckets and click on your bucket defined for your function on previous steps.

2. Open the generated report file, it should look like this:

Task 7: Create a schedule to execute the function according to a defined policy.

  1. Go to Developer Services, under Functions, select Applications and click default-apps, then Functions tab and select the fn-oci-image-purge function.

Now click on Schedules tab and create a new schedule as your need.

Note: For more details, check Scheduling a Function.

Conclusion

This blog presents a practical and safe way to control image growth in OCI Container Registry using OCI Functions. The approach combines count-based retention with protection for images currently in use by OKE, reducing operational risk and enabling predictable automation through scheduled execution and reporting.