Pushing Docker images to AWS Elastic Container Registry (ECR) from Drone CI is a common task, but it often trips people up due to the intricacies of authentication and repository creation.

Let’s see it in action. Imagine you have a simple Drone CI pipeline defined in your .drone.yml:

kind: pipeline
type: docker
name: default

steps:
  - name: build_and_push
    image: plugins/docker
    settings:
      repo: 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app
      tags:
        - latest
        - ${DRONE_COMMIT_SHA}
      username:
        from_secret: aws_access_key_id
      password:
        from_secret: aws_secret_access_key
      registry: 123456789012.dkr.ecr.us-east-1.amazonaws.com

This pipeline uses the official plugins/docker image, which is designed to handle pushing to various registries, including ECR. The settings block is where the magic (and potential pitfalls) lie.

The core problem this solves is getting your application’s Docker image from your CI environment into a secure, managed registry within AWS, ready for deployment to services like ECS or EKS. Drone CI, by default, doesn’t know how to authenticate with AWS ECR. You need to provide it with credentials and tell it where to push.

Here’s how it works under the hood for ECR:

  1. Authentication: The plugins/docker image, when configured for ECR, doesn’t just blindly use the provided username and password. It actually uses these credentials to call the AWS ECR GetAuthorizationToken API. This API returns a temporary, base64-encoded token. The plugin then decodes this token and uses it as the password for a standard Docker login against the ECR endpoint. The username is typically AWS.
  2. Repository Existence: The plugin will attempt to push to the specified repo. If the repository doesn’t exist in ECR, the push will fail. The plugins/docker does not automatically create ECR repositories. You must create them beforehand in AWS.
  3. Tagging: The tags setting allows you to specify multiple tags for your image. This is crucial for versioning and deployment strategies. You can use environment variables like ${DRONE_COMMIT_SHA} to tag with the commit hash, ensuring traceability.

The most surprising true thing about this process is that the username and password you provide to the plugins/docker are not directly used for a static Docker login. Instead, they are used to obtain a temporary, dynamic authentication token from AWS. This is a security measure to prevent long-lived credentials from being embedded directly in Docker login commands.

Let’s break down the essential components you need to control:

  • repo: This is your full ECR repository URI. It follows the pattern: <aws_account_id>.dkr.ecr.<region>.amazonaws.com/<repository_name>. For example, 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app.
  • tags: A list of tags to apply to the image. Common choices include latest and the commit SHA.
  • username: This should be an IAM user’s Access Key ID. It’s best practice to create a dedicated IAM user with only the necessary permissions for ECR.
  • password: This should be the IAM user’s Secret Access Key.
  • registry: This is the ECR endpoint URL. It’s the same as the base of your repo URI, without the repository name.

You’ll need to create these secrets in your Drone CI project. Navigate to your project’s settings in Drone, then to the "Secrets" tab. Add aws_access_key_id and aws_secret_access_key with the corresponding values from your IAM user.

Crucially, before Drone can push, you must create the ECR repository in your AWS account. You can do this via the AWS Management Console or the AWS CLI:

aws ecr create-repository --repository-name my-app --region us-east-1

Replace my-app and us-east-1 with your desired repository name and AWS region. The IAM user whose credentials you’re using in Drone must have permissions to create and push images to this repository. A policy like this is a good starting point:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ECRRepoPolicy",
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:GetRepositoryPolicy",
                "ecr:DescribeRepositories",
                "ecr:ListImages",
                "ecr:DescribeImages",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload",
                "ecr:PutImage"
            ],
            "Resource": "*"
        }
    ]
}

The plugins/docker image also has a repo_exists setting (defaults to true). If you set it to false, the plugin will attempt to create the repository if it doesn’t exist. However, this requires broader IAM permissions (like ecr:CreateRepository) for the user, which is often less desirable from a security perspective. It’s generally recommended to create the repository manually or via infrastructure-as-code.

The next hurdle you’ll likely encounter is understanding how to manage image lifecycles within ECR, such as setting up lifecycle policies to automatically delete old images.

Want structured learning?

Take the full Drone course →