Skip to main content

ECR

Amazon Elastic Container Registry (ECR) stores container images so that Kubernetes and Lambda can pull them. The platform keeps every image in one artifacts account, tags them immutably, and lets the workload accounts pull but only the CI account push.

What it does

A repository in ECR holds the versions of one image. Each version is a manifest identified by a digest and referenced by one or more tags. With tag immutability on, a tag can be added to a manifest but never moved to a different one, so main-a1b2c3d always means the same build. Scan on push checks each new image for known vulnerabilities. A lifecycle policy expires old images automatically. A repository policy grants other accounts pull or push rights. Images are encrypted at rest.

How BuiltForProd uses it

The ecr unit (registry module terraform-aws-modules/ecr 3.2.1 wrapped in modules/ecr, listed by core/artifacts/us-west-2) creates the repositories in core-artifacts. It ships with enable_ecr = false and is switched on when the code repositories are deployed; until then no repository exists. Two repositories are defined:

RepositoryImagePulled by
acme-aws-blueprint-webappThe Flask API image built by the web application code repositoryEKS nodes in every plat-* account
acme-aws-blueprint-etl-lambdaThe container image for the ETL trigger functionThe Lambda service in every plat-* account

Every repository has the same settings: IMMUTABLE tags, scan on push, KMS encryption, and a three-rule lifecycle policy. Untagged images are expired after 14 days. Release images, the ones carrying a v tag, are kept indefinitely. Only integration builds, tagged main-<sha>, age out, and the last 30 of those are kept. The order matters: ECR expires an image by at most the first rule that claims it, and a release image is a promoted main-<sha> manifest with the release tag added, so it matches both rules and the release rule wins. The repository policy grants read to the four plat-* accounts and to Lambda in those accounts, and read-write to core-auto, where the GitHub OIDC deployer roles for the code repositories carry an ecr_push policy scoped to their own repository.

Immutability is what makes the delivery model work. Each image is built once on main and pushed as main-<short sha>; promotion adds a release tag to the same manifest rather than rebuilding; nothing ever pushes latest. Production runs the exact bytes that were tested in staging, and the prod promotion workflow checks that the release tag exists in ECR before opening the rollout pull request. The ETL blueprint records the deployed tag in Parameter Store and seeds a bootstrap tag once, because its Lambda module cannot reference a moving tag.

Images are scanned twice before and once after they land: the code repositories' CI runs a Trivy scan that fails on fixable HIGH and CRITICAL vulnerabilities, ECR scans on push, and Inspector, when switched on, rescans stored images as new vulnerabilities are published (about $0.09 per image). Images run as a fixed non-root user, UID 10001, matching the Kubernetes chart's security context.

Terms you will see

TermMeaning
RepositoryThe ECR container for one image's versions.
Manifest and digestThe image content and its content hash; the thing a tag points at.
Tag immutabilityA tag, once set, cannot be pointed at a different manifest.
Scan on pushVulnerability scanning of each image as it arrives.
Lifecycle policyRules that expire old or untagged images; release tags are exempt.
Repository policyThe resource policy granting other accounts pull or push.
main-<sha> and release tagThe build tag and the promotion tag on the same image.

Where to read more