I have a private AWS ECR which holds my private docker image. I'm pulling and running this image from within an EC2 in the same account as the private repo. ECR Helper is installed, configured, and working properly as I can pull and run the image
The issue I'm having is related specifically to the docker compose up
command using the --pull
argument. If I update the image and push to ECR with the same tag, in this case "2.0.0.0-dev", I can see that the image is pushed properly and the hash has changed but when I run docker compose up -d --pull missing --quiet-pull --force-recreate
it does not detect the image as missing or updated so it does not pull the latest before recreating the container. If I run docker compose pull
, it will detect the image has changed and properly pull the latest
Am I missing something or doing something wrong here? From what I understand the --pull argument with missing
should act the same as docker compose pull
. Maybe I'm missing a required permission in the IAM policy? Is the "latest" tag treated differently than other tags?
I've tried enabling debug logging for docker but the logs don't tell me much. While testing, I'm running these commands locally on the EC2 instance but this will eventually be used in an ansible script to restart and update the container/image if anything changes, so using --pull always
wouldn't be ideal since the ansible script could deploy changes that only require a container restart and not a fresh pull every time. I mainly just want to understand what's going on here because I could work around this by just adding an ansible command to first run docker compose pull
then do the restart; it would also be nice for this to work in 1 command vs 2
This is the IAM instance profile used with the EC2
{
"Statement": [
{
"Action": "ecr:GetAuthorizationToken",
"Effect": "Allow",
"Resource": "*"
},
{
"Action": [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:DescribeImages"
],
"Effect": "Allow",
"Resource": "arn:aws:ecr:us-west-2:XXXXXXXXXXXX:repository/my_app"
}
],
"Version": "2012-10-17"
}
compose.yaml
---
services:
my_app:
image: XXXXXXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/my_app:2.0.0.0-dev
container_name: my_app
restart: always
environment:
PUID: 1001
PGID: 1001
CONNECTION_STRING_FILE: /run/secrets/connection_string
SLACK_APP_TOKEN_FILE: /run/secrets/slack_app_token
SLACK_BOT_TOKEN_FILE: /run/secrets/slack_bot_token
secrets:
- connection_string
- slack_app_token
- slack_bot_token
volumes:
- ./config/:/config:ro
- /var/log/my_app/:/logs/
secrets:
slack_app_token:
file: "/etc/my_app/slack_app_token"
slack_bot_token:
file: "/etc/my_app/slack_bot_token"
connection_string:
file: "/etc/my_app/connection_string"
Looking at what you've shared, let me explain what's happening with your Docker Compose setup. The behavior you're seeing isn't related to IAM permissions or the ECR configuration, but it’s actually about how Docker Compose handles the --pull missing
flag.
When you use --pull missing
, Docker Compose only checks if the image tag exists locally. It doesn't verify whether the remote image with the same tag has been updated. That’s why docker compose pull
works (it explicitly checks the registry) while --pull missing
doesn't catch the update.
Of course, while you could modify your compose file to use pull_policy: always
, this would cause unnecessary pulls every time as you mentioned, which isn't ideal.
Since you mentioned wanting to use Ansible down the line, here's an approach I suggest, which directly compares the ECR and local image digests:
- name: Check and update container if needed
block:
- name: Get the latest image digest from ECR
shell: |
aws ecr describe-images --repository-name my_app --image-ids imageTag=2.0.0.0-dev --query 'imageDetails[0].imageDigest' --output text
register: ecr_digest
- name: Get the local image digest
shell: |
docker inspect --format='{{index .RepoDigests 0}}' XXXXXXXXXXXX.dkr.ecr.us-west-2.amazonaws.com/my_app:2.0.0.0-dev | cut -d "@" -f2
register: local_digest
ignore_errors: yes # In case the image isn't there yet
- name: Pull and restart if image has changed
when: ecr_digest.stdout != local_digest.stdout or local_digest.rc is defined
shell: docker compose up -d --pull always --force-recreate
- name: Log update status
debug:
msg: "{{ 'Container updated with new image' if (ecr_digest.stdout != local_digest.stdout or local_digest.rc is defined) else 'Already running latest version' }}"
But in the interim, while you're testing locally on the EC2, you'll need to run the two commands separately as you suggested: first docker compose pull
to check for updates, then docker compose up
to restart the container.