Skip to content

Using AWS ECR as a Universal OCI Repository

Written by:

Igor Gorovyy
DevOps Engineer Lead & Senior Solutions Architect

LinkedIn


w

Introduction

In the modern world of DevOps and Cloud Native development, artifact management is becoming increasingly complex. Each type of artifact traditionally required a separate repository: Docker Hub for containers, Artifactory for binaries, MLflow for machine learning models, etc. This created a "zoo" of services that is difficult to maintain and scale.

Thanks to the emergence of the Open Container Initiative (OCI), a unified standard for all types of artifacts has appeared. This has allowed companies to create universal repositories that can store any artifacts: from Docker images to machine learning models.

Amazon Elastic Container Registry (ECR) is not just a registry for Docker images, but a full-fledged OCI-compliant repository. Since 2022, AWS ECR has supported the OCI Artifact specification, allowing it to store any artifacts that comply with this standard.

In this article, I will cover: - How to effectively use AWS ECR for different types of artifacts - How to eliminate the need for multiple repositories - Practical examples of working with different types of artifacts - Best practices and security recommendations

Benefits of Using ECR as a Universal Repository

  1. Single Access Point: All artifacts in one place with a unified authentication system
  2. AWS Integration: Native support for IAM, CloudWatch, KMS
  3. Cost Effectiveness: Single billing system and cost optimization
  4. Simplified Management: Common lifecycle policies and replication
  5. Security: Built-in vulnerability scanning and encryption

Prerequisites

Before we dive into practical examples, let's set up the necessary tools and create our first repository.

Required Tools

For working with ECR as a universal repository, we'll need:

  • AWS CLI: For interacting with AWS services
  • Helm CLI: For working with Helm charts
  • ORAS CLI: For working with OCI artifacts
  • Terraform >=1.5: For infrastructure as code

Creating and Configuring the Repository

First, let's create a repository in ECR:

aws ecr create-repository --repository-name my-universal-repo \
  --image-scanning-configuration scanOnPush=true \
  --region eu-central-1

This repository will be our universal storage for all types of artifacts.

Authentication

To work with ECR, you need to authenticate:

aws ecr get-login-password \
  | docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.eu-central-1.amazonaws.com

Working with Different Types of Artifacts

Let's look at how to work with different types of artifacts in our universal repository.

1. Docker Images

Docker images are the classic use case for ECR. The process of working with them is simple and straightforward:

# Build
docker build -t my-app .

# Tag
docker tag my-app:latest <aws_id>.dkr.ecr.eu-central-1.amazonaws.com/my-universal-repo:my-app-v1

# Push
docker push <aws_id>.dkr.ecr.eu-central-1.amazonaws.com/my-universal-repo:my-app-v1

Key benefits of using ECR for Docker images: - Automatic vulnerability scanning - Immutable tags - Integration with ECS/EKS - Cross-region replication capability

2. Helm Charts via OCI

Helm 3 added OCI support, allowing charts to be stored directly in ECR without the need for a separate Helm repository.

# Enable OCI support
export HELM_EXPERIMENTAL_OCI=1

# Create helm chart
helm create my-chart

# Package
helm package my-chart

# Push to ECR
helm push my-chart-0.1.0.tgz oci://<aws_id>.dkr.ecr.eu-central-1.amazonaws.com/my-universal-repo

# Install
helm install mychart oci://<aws_id>.dkr.ecr.eu-central-1.amazonaws.com/my-universal-repo --version 0.1.0

Benefits of storing Helm charts in ECR: - Versioning and access control - Single access point with Docker images - Possibility to use in GitOps processes

3. Terraform Providers and Modules

With version 1.5, Terraform added native support for OCI registries for modules. This allows using ECR as a source for modules and providers.

3.1. Publishing Terraform Provider/Module

# Module structure
tree my-module/
├── main.tf
├── variables.tf
├── outputs.tf

# Archive
tar -czf my-module.tar.gz my-module/

# Push using oras
oras push <aws_id>.dkr.ecr.eu-central-1.amazonaws.com/my-universal-repo:tf-my-module-v1 \
  --artifact-type application/vnd.terraform.module \
  my-module.tar.gz

3.2. Using as a Module in Terraform

module "my_module" {
  source = "oci::<aws_id>.dkr.ecr.eu-central-1.amazonaws.com/my-universal-repo:tf-my-module-v1"
}

Benefits of storing Terraform modules in ECR: - Versioning and access control - Private modules without additional services - Integration with existing CI/CD pipelines

4. ML Models

One of the most interesting capabilities is storing machine learning models. ECR can replace specialized ML registries for many use cases.

Basic Example:

# Upload model
oras push <aws_id>.dkr.ecr.eu-central-1.amazonaws.com/my-universal-repo:ml-my-model-v1 \
  --artifact-type application/vnd.model.mlflow \
  model.pkl

Advanced Example with Metadata:

oras push <aws_id>.dkr.ecr.eu-central-1.amazonaws.com/my-universal-repo:ml-my-model-v2 \
  --artifact-type application/vnd.model \
  "model.onnx" \
  "metadata.json"

📌 It's important to understand that ECR doesn't validate the MIME type - it just stores the artifact with the specified type. However, other tools can use this type for filtering and identifying artifacts.

Working with ML Models in Detail

Metadata Structure

For effective ML model management, it's important to have well-structured metadata. Here's an example:

{
  "model_name": "resnet50",
  "version": "1.0.0",
  "framework": "onnx",
  "input_shape": [1, 3, 224, 224],
  "output_classes": 1000,
  "trained_on": "imagenet",
  "accuracy_top1": 0.76,
  "accuracy_top5": 0.93,
  "created_at": "2025-07-08T12:00:00Z"
}

Such metadata allows: - Tracking model versions - Documenting parameters and characteristics - Simplifying search and selection of needed versions - Automating deployment processes

Model Publication Automation

To simplify the model publication process, you can use a script:

#!/bin/bash

set -e

# === Arguments ===
MODEL_FILE=${1:-"model.onnx"}
META_FILE=${2:-"metadata.json"}
TAG=${3:-"resnet50-v1"}

# === AWS ECR Configuration ===
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=eu-central-1
REPO_NAME=ml-registry
REPO_URL="$AWS_ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/$REPO_NAME"

# === Authentication ===
aws ecr get-login-password \
  | oras login --username AWS --password-stdin $REPO_URL

# === Push ===
echo "🚀 Uploading model [$MODEL_FILE] and metadata [$META_FILE] to [$REPO_URL:$TAG]"

oras push $REPO_URL:$TAG \
  --artifact-type application/vnd.model \
  "$MODEL_FILE" \
  "$META_FILE"

echo "✅ Done. Artifact pushed as $REPO_URL:$TAG"

Common MIME Types for ML Artifacts

When working with ML models, it's important to specify the correct MIME types:

MIME Type Purpose
application/vnd.model General ML artifact
application/vnd.model.mlflow MLflow model
application/vnd.model.onnx ONNX model
application/vnd.model.torch TorchScript
application/vnd.model.keras Keras model

Working with YOLO Models

Working with popular computer vision models like YOLO is of particular interest. There are three main approaches to storing them in ECR:

FROM ultralytics/yolov5:latest

# Add our model
COPY best.pt /models/best.pt

CMD ["python3", "detect.py", "--weights", "/models/best.pt", "--source", "0"]

Build and publish:

docker build -t yolo-inference .
docker tag yolo-inference <aws_id>.dkr.ecr.eu-central-1.amazonaws.com/yolo:v5
docker push <aws_id>.dkr.ecr.eu-central-1.amazonaws.com/yolo:v5

2. As an OCI Artifact via ORAS

oras push <ecr_repo>:yolov5-v1 \
  --artifact-type application/vnd.model.pytorch \
  best.pt \
  metadata.json

Metadata for YOLO model:

{
  "model_name": "yolov5",
  "framework": "pytorch",
  "version": "6.2",
  "input_shape": [1, 3, 640, 640],
  "trained_on": "coco",
  "classes": 80
}

3. As an ONNX Model

YOLOv5 supports export to ONNX format:

python export.py --weights best.pt --include onnx

Publishing ONNX version:

oras push <ecr_repo>:yolov5-onnx \
  --artifact-type application/vnd.model.onnx \
  best.onnx \
  metadata.json

Approach Comparison

Format Pros Cons
Docker image - Ready to run in ECS/EKS/Lambda
- Includes all dependencies
- Easier deployment
- Large size
- Less flexibility
- Harder model updates
.pt + ORAS - Compact size
- Convenient as model registry
- Easy versioning
- Requires separate runtime environment
- Additional environment configuration
ONNX + ORAS - Cross-framework compatibility
- Optimized inference
- Easy integration
- Not all YOLO features available
- Possible performance differences

Security and Access Management

IAM Policies

Basic policy for read/write access to ECR:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage",
        "ecr:BatchCheckLayerAvailability",
        "ecr:PutImage",
        "ecr:InitiateLayerUpload",
        "ecr:UploadLayerPart",
        "ecr:CompleteLayerUpload"
      ],
      "Resource": "*"
    }
  ]
}

Cross-Account Access

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCrossAccountPull",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::444455556666:root"
      },
      "Action": [
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer"
      ]
    }
  ]
}

Cost Management and Optimization

ECR pricing consists of three components:

  1. Storage: $0.10 per GB-month
  2. Data Transfer:
  3. Inbound: Free
  4. Outbound: Based on AWS region and volume
  5. API Requests:
  6. First 10 million requests per month: $0.01 per request
  7. Over 10 million requests: $0.008 per request

Cost Optimization Tips

  • Use lifecycle policies to remove unused artifacts
  • Implement image tagging strategy
  • Consider multi-region replication costs
  • Use AWS Organizations for consolidated billing
  • Monitor costs with AWS Cost Explorer

CI/CD Integration

GitHub Actions Example

name: Publish to ECR

on:
  push:
    branches: [ main ]
    paths:
      - 'models/**'
      - 'charts/**'
      - 'terraform/**'

jobs:
  publish:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: eu-central-1

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1

    - name: Publish artifacts
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        ECR_REPOSITORY: my-universal-repo
        IMAGE_TAG: ${{ github.sha }}
      run: |
        # Publish Docker images
        if [ -f Dockerfile ]; then
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        fi

        # Publish Helm charts
        if [ -d charts ]; then
          for chart in charts/*; do
            if [ -d "$chart" ]; then
              helm package $chart
              helm push ${chart##*/}-*.tgz oci://$ECR_REGISTRY/$ECR_REPOSITORY
            fi
          done
        fi

        # Publish ML models
        if [ -d models ]; then
          for model in models/*.onnx; do
            if [ -f "$model" ]; then
              oras push $ECR_REGISTRY/$ECR_REPOSITORY:${model##*/}-$IMAGE_TAG \
                --artifact-type application/vnd.model.onnx \
                "$model" \
                "${model%.*}.json"
            fi
          done
        fi

Terraform Configuration

resource "aws_ecr_repository" "universal_repo" {
  name                 = "my-universal-repo"
  image_tag_mutability = "IMMUTABLE"

  image_scanning_configuration {
    scan_on_push = true
  }

  encryption_configuration {
    encryption_type = "KMS"
    kms_key = aws_kms_key.ecr_key.arn
  }

  tags = {
    Environment = "production"
    Purpose     = "universal-artifacts"
  }
}

resource "aws_ecr_lifecycle_policy" "cleanup_policy" {
  repository = aws_ecr_repository.universal_repo.name

  policy = jsonencode({
    rules = [
      {
        rulePriority = 1
        description  = "Keep last 10 versions of each artifact"
        selection = {
          tagStatus     = "tagged"
          tagPrefixList = ["v"]
          countType     = "imageCountMoreThan"
          countNumber   = 10
        }
        action = {
          type = "expire"
        }
      },
      {
        rulePriority = 2
        description  = "Remove inactive artifacts older than 90 days"
        selection = {
          tagStatus = "untagged"
          countType = "sinceImagePushed"
          countUnit = "days"
          countNumber = 90
        }
        action = {
          type = "expire"
        }
      }
    ]
  })
}

Conclusions and Recommendations

  1. Structure Planning:
  2. Develop a clear naming strategy
  3. Define lifecycle policies
  4. Document processes and standards

  5. Security:

  6. Use least privilege access
  7. Regularly update security policies
  8. Configure vulnerability scanning

  9. Automation:

  10. Create repeatable processes
  11. Use CI/CD for publication
  12. Automate testing and validation

  13. Monitoring:

  14. Track resource usage
  15. Set up alerts
  16. Regularly analyze costs

AWS ECR as a universal OCI repository provides a powerful and flexible platform for managing various artifacts. Proper setup and usage can significantly simplify development and deployment processes while ensuring a high level of security and control.