Nota revisada y reescrita el 18 de octubre de 2026: la versión original tenía errores y datos sin fuente.
La build pasa, la imagen se construye y el docker push a Amazon ECR falla con no basic auth credentials, denied: Your authorization token has expired o un AccessDeniedException. Casi siempre es una de dos cosas: el rol de IAM no tiene los permisos bien escritos, o Docker nunca se autenticó contra el registro.
1. La política IAM
Para subir una imagen hacen falta estas acciones:
ecr:GetAuthorizationTokenecr:BatchCheckLayerAvailabilityecr:InitiateLayerUploadecr:UploadLayerPartecr:CompleteLayerUploadecr:PutImage
La trampa está en la primera: GetAuthorizationToken no se puede limitar a un repositorio. Pide un token para el registro entero, así que su Resource tiene que ser "*". Si la metes en el mismo bloque que las demás con el ARN del repositorio, la llamada falla aunque el resto esté bien. Lo correcto son dos bloques:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:PutImage"
],
"Resource": "arn:aws:ecr:REGION:ACCOUNT_ID:repository/NOMBRE_REPO"
}
]
}
Si Jenkins corre en una instancia EC2, asigna esta política a un rol y el rol a la instancia (instance profile). Así no hay claves de acceso guardadas en Jenkins.
2. El login de Docker
Tener el rol en la instancia no autentica a Docker. Docker solo sabe hablar con el registro si alguien le da un usuario y una contraseña, y en ECR la contraseña es un token temporal que caduca a las 12 horas. Hay que pedirlo en cada build, antes del push:
pipeline {
agent any
environment {
REGION = 'eu-west-1'
REGISTRY = '123456789012.dkr.ecr.eu-west-1.amazonaws.com'
IMAGE = "${REGISTRY}/mi-app:${env.BUILD_NUMBER}"
}
stages {
stage('Build') {
steps { sh 'docker build -t $IMAGE .' }
}
stage('Push') {
steps {
sh '''
aws ecr get-login-password --region $REGION \
| docker login --username AWS --password-stdin $REGISTRY
docker push $IMAGE
'''
}
}
}
}
El usuario es literalmente AWS. --password-stdin evita que el token aparezca en la lista de procesos o en el log de Jenkins.
Si prefieres no hacer login a mano, el Amazon ECR credential helper (docker-credential-ecr-login) obtiene el token solo, usando el rol de la instancia. Se configura en ~/.docker/config.json del usuario que ejecuta Docker en el agente de Jenkins:
{ "credHelpers": { "123456789012.dkr.ecr.eu-west-1.amazonaws.com": "ecr-login" } }
Cómo saber cuál de las dos es
aws sts get-caller-identityen un paso del pipeline te dice qué identidad está usando de verdad: puede no ser el rol que crees.aws ecr get-login-passwordfalla conAccessDeniedException: el problema es la política. Mira elResourcedeGetAuthorizationToken.- El login funciona pero el push devuelve
denied: faltan acciones de subida de capas, o el ARN del repositorio no coincide (región, cuenta o nombre). no basic auth credentials: Docker no hizo login, o lo hizo otro usuario del sistema distinto al que ejecuta el push.name unknown: The repository ... does not exist: el repositorio no existe. Créalo antes conaws ecr create-repository, Terraform o la consola. Otra opción: desde diciembre de 2025, ECR puede crearlo al hacer push (create on push), pero solo si hay una repository creation template cuyo prefijo coincida con el nombre. Sin plantilla, el push falla igual.