Guide to pushing docker images to AWS ECR

Introduction

Amazon ECR (Elastic Container Registry) is a fully managed Docker Registry (image repository) by AWS. Instead of having to operate your own Docker Hub or install a Registry on a server, you use ECR to store, manage, and deploy Docker Container Images in a highly secure and scalable manner.

Advantages

  • Deep integration with AWS: ECR works most seamlessly with Amazon EKS (Kubernetes) and ECS. You only need to declare the image path, and AWS will handle the authentication and image pulling (pull) automatically.
  • Absolute security: Use AWS IAM for authorization. Only authorized Services or Users can push/pull images. Images are also automatically encrypted at rest.
  • Image Scanning: ECR automatically checks the images you push to find any libraries with security vulnerabilities (CVE), giving you more peace of mind about your code.
  • Lifecycle Policies: You can set up automatic deletion of old images and untagged images to avoid wasting storage space.
  • High performance: Since images are stored on AWS infrastructure (S3), pulling images to EC2 or EKS servers is very fast and stable.

Pricing (Pay-as-you-go)

AWS ECR has no upfront fees or fixed monthly maintenance fees. You only pay for what you actually use:

  • Storage:
    • Approximately $0.10 per GB/month for Private Repositories.
    • Example: If you store 10GB of images, you pay about $1 per month.
  • Data Transfer Out:
    • Free: If you pull images from ECR to AWS services within the same Region (e.g., ECR and EKS are both in Singapore).
    • Fees apply: If you pull images from ECR to your local machine or to another Region (calculated according to standard AWS Data Transfer fees, about $0.09/GB after the free tier limit).
  • Free Tier:
    • Public Repository: 50 GB of storage free per month (forever).
    • Private Repository: 500 MB of storage free per month for the first 12 months after account registration.

Detail

In this article, I will guide you through 2 ways to push docker images to AWS ECR

Method 1: Manual approach

For this method, the process is similar to pushing a docker image to Docker Hub or any other repository, simply including the steps:

  1. Build docker image
  2. Login to docker repository
  3. Push docker image


As in the previous examples where I am using NestJS, create a Dockerfile with the following content (you can change it according to your needs)

FROM node:22-alpine
WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .
RUN npm run build

EXPOSE 3000
CMD ["npm", "run", "start:prod"]


Next is the .dockerignore file; if you do not need it, you can skip it

/dist
/logs
/node_modules
/test

.env
.gitignore
.prettierrc.js
docker-compose.yml
Dockerfile
README.md


Next, build the docker image:

docker build -t {image name} .


After building successfully, you must tag the new docker image appropriately before you can push it to AWS ECR

docker tag {image name}:{tag} {aws_account_id}.dkr.ecr.{region}.amazonaws.com/{image name}:{tag}

# example
docker tag nestjs-app:latest 758222924841.dkr.ecr.ap-southeast-1.amazonaws.com/nestjs-app:latest


Next is logging into ECR

aws ecr get-login-password --region {region} | docker login --username AWS --password-stdin {aws_account_id}.dkr.ecr.{region}.amazonaws.com

# example
aws ecr get-login-password --region ap-southeast-1 | docker login --username AWS --password-stdin 758222924841.dkr.ecr.ap-southeast-1.amazonaws.com
Login Succeeded

  • The --username AWS part is fixed and does not need to be changed
  • Note that you need to setup aws_access_key_id and aws_secret_access_key and have the corresponding permissions to be able to log in


Next is creating a repository with a name corresponding to the docker image

aws ecr create-repository --repository-name {image name} --region {region}

# exmaple
aws ecr create-repository --repository-name nestjs-app --region ap-southeast-1
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:ap-southeast-1:758222924841:repository/nestjs-app",
        "registryId": "758222924841",
        "repositoryName": "nestjs-app",
        "repositoryUri": "758222924841.dkr.ecr.ap-southeast-1.amazonaws.com/nestjs-app",
        "createdAt": "2025-12-19T20:04:16.259000+07:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": false
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}


Then just push as usual; note that you must push the docker image after it has been tagged

docker push 758222924841.dkr.ecr.ap-southeast-1.amazonaws.com/nestjs-app:latest
The push refers to repository [758222924841.dkr.ecr.ap-southeast-1.amazonaws.com/nestjs-app]
da16efb01857: Pushed
2b752f7c71fd: Pushed
fc1c5222d85f: Pushed
be77d4576856: Pushed
d1c5487580c4: Pushed
f98f71470e11: Pushed
8f2a5f9b793e: Pushed
8de20ec00463: Pushed
589002ba0eae: Pushed
8d513d1f314d: Pushed
latest: digest: sha256:9a964a6ce1573d5aa1fa10be0ad3f1a32d753379e1368b10c684f7d6b7d4050b size: 856


Method 2: Using AWS CDK

Since AWS CDK is IaC, it also supports implementation to push docker images to ECR automatically. Create the file lib/ecr-stack.ts:

import * as cdk from "aws-cdk-lib"
import * as path from "path"
import * as ecr from "aws-cdk-lib/aws-ecr"
import { DockerImageAsset, Platform } from "aws-cdk-lib/aws-ecr-assets"
import { Construct } from "constructs"

export class EcrStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props)

    const nestJsImage = new DockerImageAsset(this, "DockerImage", {
      directory: path.join(__dirname, "./path-to-project"),
    })

    const repo = nestJsImage.repository as ecr.Repository
    repo.addLifecycleRule({
      maxImageAge: cdk.Duration.days(30),
      tagStatus: ecr.TagStatus.ANY,
    })

    new cdk.CfnOutput(this, "ImageUri", {
      value: nestJsImage.imageUri,
      description: "URI of the image in ECR",
    })
  }
}

  • directory: is the path to the code base; with just this, CDK will automatically find the Dockerfile in the project to build the docker image and push it to ECR (note that your machine must have docker running to use this)
  • addLifecycleRule: adds a rule to automatically delete old images after 30 days, because using private repositories incurs storage fees


Update bin/aws-cdk.ts

#!/usr/bin/env node
import * as cdk from "aws-cdk-lib/core"
import { EcrStack } from "../lib/ecr-stack"

const app = new cdk.App()
new EcrStack(app, "EcrStack")


After deploying, the results are as follows:

 EcrStack
 Deployment time: 79.78s

Outputs:
EcrStack.ImageUri = 758222924841.dkr.ecr.ap-southeast-1.amazonaws.com/cdk-hnb659fds-container-assets-758222924841-ap-southeast-1:35e3cfdaa7395cdc051c3a6eb6c8390d0c1ffde54debfa49d6c60865ae98a60d

 Total time: 92.64s


You can check the successfully uploaded images on the AWS Console

Happy coding!

See more articles here.

Comments

Popular posts from this blog

All practice series

Deploying a NodeJS Server on Google Kubernetes Engine

Setting up Kubernetes Dashboard with Kind

Using Kafka with Docker and NodeJS

Monitoring with cAdvisor, Prometheus and Grafana on Docker

Kubernetes Practice Series

Kubernetes Deployment for Zero Downtime

Practicing with Google Cloud Platform - Google Kubernetes Engine to deploy nginx

NodeJS Practice Series

Helm for beginer - Deploy nginx to Google Kubernetes Engine