Introduction
To pull a private image from Amazon ECR into a local Kubernetes cluster, you need two things: valid ECR credentials and a way for the cluster's container runtime to use them. The usual Kubernetes-friendly path is to create an image pull secret and reference it from the workload.
That solves the problem for kind, minikube, k3d, and other local clusters as long as the nodes can reach the ECR endpoint. The main operational sharp edge is that ECR login tokens expire, so a setup that worked yesterday can fail later unless the secret is refreshed.
Authenticate to ECR First
Start by confirming you can pull the image from your local machine. If Docker cannot authenticate locally, the cluster will not be able to pull it either.
1AWS_ACCOUNT_ID="123456789012"
2AWS_REGION="us-east-1"
3ECR_REGISTRY="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
4
5aws ecr get-login-password --region "$AWS_REGION" \
6| docker login --username AWS --password-stdin "$ECR_REGISTRY" docker pull "$ECR_REGISTRY/my-service:latest" ``` That first step proves three things: - the registry URL is correct - your AWS credentials can request an ECR login token - the repository and tag actually exist If this local pull fails, fix that before touching Kubernetes. ## Create a Kubernetes Image Pull Secret Pods do not automatically reuse your laptop's Docker login state. They need credentials in the namespace where the workload runs: ```bash NAMESPACE="default" SECRET_NAME="ecr-pull-secret" kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - kubectl create secret docker-registry "$SECRET_NAME" \ --docker-server="$ECR_REGISTRY" \ --docker-username=AWS \ --docker-password="$(aws ecr get-login-password --region "$AWS_REGION")" \ --namespace "$NAMESPACE" ``` That creates a `kubernetes.io/dockerconfigjson` secret that Kubernetes can use during image pulls. ## Reference the Secret in the Workload The deployment has to point at the secret explicitly through `imagePullSecrets`: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-service namespace: default spec: replicas: 1 selector: matchLabels: app: my-service template: metadata: labels: app: my-service spec: imagePullSecrets: - name: ecr-pull-secret containers: - name: app image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-service:latest imagePullPolicy: Always ports: - containerPort: 8080 ``` Apply the workload and inspect pod events if it fails: ```bash kubectl apply -f deployment.yaml kubectl get pods -n default kubectl describe pod -n default <pod-name> ``` `kubectl describe pod` is usually the fastest way to distinguish authentication errors, tag-not-found errors, and plain network failures. ## Local Cluster Alternatives If you are using a very local workflow such as `kind` or `minikube`, another option is to pull the image locally and then preload it into the cluster nodes instead of relying on runtime registry pulls. That is convenient for rapid development, but it is less representative of how real remote pulls work. For a more realistic setup, keep using `imagePullSecrets` and let the nodes pull from ECR directly. ## Handle Token Expiration ECR login tokens expire. That means the secret eventually becomes stale. If image pulls start failing after working previously, expired credentials are one of the first things to check. A simple refresh flow looks like this: ```bash kubectl delete secret ecr-pull-secret -n default --ignore-not-found kubectl create secret docker-registry ecr-pull-secret \ --docker-server="$ECR_REGISTRY" \ --docker-username=AWS \ --docker-password="$(aws ecr get-login-password --region "$AWS_REGION")" \ -n default ``` In shared development clusters, teams often automate that refresh with CI or scheduled admin jobs. ## Common Pitfalls The most common mistake is creating the pull secret in one namespace and deploying the pods in another. Kubernetes secrets are namespace-scoped, so the pod cannot use a secret from a different namespace. Another common issue is incorrect image naming. The registry host, repository path, and tag must match the ECR repository exactly. People also forget that local clusters still have their own node runtime and network environment. If the nodes cannot reach ECR because of VPN, DNS, or proxy issues, valid credentials alone will not help. Finally, do not ignore token expiry. If pulls suddenly fail after previously working, refreshing the ECR secret is often the fix. ## Summary - Authenticate to ECR locally first and confirm a direct `docker pull` works. - Create a Kubernetes image pull secret in the same namespace as the workload. - Reference that secret through `imagePullSecrets` in the pod spec. - Use `kubectl describe pod` to diagnose pull failures quickly. - Refresh ECR-backed secrets when the underlying login token expires.