Merged
Size
S
Change Breakdown
Feature85%
Docs15%
#3467feat(webapp): apply default repository policy on ECR repo creation

Custom IAM policies attach to new ECR repositories

Custom IAM policies attach to new ECR repositories

Self-hosted deployments can automatically apply custom IAM policies to newly created ECR repositories, ensuring worker nodes in separate AWS accounts can pull images.

Self-hosted environments often operate with isolated AWS accounts, placing the Elastic Container Registry (ECR) in a shared platform account while worker nodes run in separate cluster accounts. By default, newly created ECR repositories restrict read access to the registry owner, causing worker nodes to fail with a 403 Forbidden error during initial deployments.

Custom IAM policies can now be attached to new ECR repositories automatically. By providing a JSON policy through a new environment variable, cross-account pull access is granted immediately upon repository creation in the webapp. Existing repositories are also reconciled during subsequent deployments to ensure access configurations stay synchronized and self-heal from partial configuration failures.

View Original GitHub Description

Summary

Self-hosters that operate the webapp's ECR account separately from the account running the EKS workers (e.g., a shared platform account that hosts the registry plus per-team accounts that host clusters) currently hit a 403 Forbidden the first time any project is deployed:

Failed to pull image "<acct-A>.dkr.ecr.<region>.amazonaws.com/<namespace>/proj_…:…":
unexpected status from HEAD request to .../v2/.../manifests/sha256:…: 403 Forbidden

ensureEcrRepositoryExists in apps/webapp/app/v3/getDeploymentImageRef.server.ts calls CreateRepository and PutLifecyclePolicy, but never SetRepositoryPolicy — so the new repo inherits the AWS default (only the registry-owner account can read/pull). Workers in the cluster account get 403 every single deploy. The only workarounds today are running a one-off post-create script or pre-creating every repo by hand.

Proposed change

Add an optional env var:

DEPLOY_REGISTRY_ECR_DEFAULT_REPOSITORY_POLICY  (V4 mirror: V4_DEPLOY_REGISTRY_ECR_DEFAULT_REPOSITORY_POLICY)

Raw IAM policy JSON. When set, the webapp calls SetRepositoryPolicy immediately after CreateRepository so every new repo carries that policy from creation. Operators control the principal/actions; we don't bake in any opinions about cross-account boundaries.

Example value (for the typical self-host case — grant pull to the cluster account):

{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "AllowClusterAccountPull",
    "Effect": "Allow",
    "Principal": {"AWS": "arn:aws:iam::<cluster-account-id>:root"},
    "Action": [
      "ecr:GetDownloadUrlForLayer",
      "ecr:BatchGetImage",
      "ecr:BatchCheckLayerAvailability"
    ]
  }]
}

Why env var (not a chart-level field)

  • Mirrors the shape of the sibling vars (DEPLOY_REGISTRY_ECR_TAGS, DEPLOY_REGISTRY_ECR_ASSUME_ROLE_ARN, etc.) which are already operator-supplied via webapp.extraEnvVars in self-host setups.
  • Cloud is unaffected — the env var is optional, unset by default; existing behavior unchanged.
  • Existing repos are unaffected — only newly-created repos get the policy.
  • RepositoryCreationTemplate from the AWS provider isn't an alternative here: it only applies to repos created via pull-through-cache or replication, not to ecr:CreateRepository API calls.

Implementation

  • apps/webapp/app/env.server.ts — declare DEPLOY_REGISTRY_ECR_DEFAULT_REPOSITORY_POLICY and the V4 fallback.
  • apps/webapp/app/v3/registryConfig.server.ts — propagate ecrDefaultRepositoryPolicy to RegistryConfig.
  • apps/webapp/app/v3/getDeploymentImageRef.server.tscreateEcrRepository accepts the policy; if set, calls SetRepositoryPolicy after PutLifecyclePolicy.
  • docs/self-hosting/env/webapp.mdx — documentation row added under Deploy & Registry.

Verification

Verified end-to-end against a self-hosted Trigger.dev on EKS where the ECR account is separate from the cluster account:

  • Without the env var (current main): the new project's first run pod stays in ImagePullBackOff with 403 Forbidden.
  • With the env var set to a JSON granting ecr:BatchGetImage/GetDownloadUrlForLayer/BatchCheckLayerAvailability to the cluster account: a fresh trigger.dev deploy --env prod followed by a hello-world run completes in ~5s end-to-end on the first try.

Manually also confirmed that existing repos are untouched (the call only fires inside createEcrRepository, which only runs when DescribeRepositories returned RepositoryNotFoundException).

Out of scope

  • Chart values surface for this — operators already pass the existing ECR vars via webapp.extraEnvVars, so this follows the same pattern. Happy to add a first-class chart field in a follow-up if that's the preferred direction.
  • IAM-policy validation in the webapp — we forward the JSON verbatim to AWS and surface AWS's error messages on misuse, matching how DEPLOY_REGISTRY_ECR_TAGS is handled today.

This is a draft pending CI / CodeRabbit pass — happy to iterate on direction (e.g., split into per-action env vars, or extend the chart values schema) if any of the above choices feels off.

© 2026 · via Gitpulse