Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Container Registries

Rise generates temporary credentials for pushing container images to registries. The backend acts as a credential broker, abstracting provider-specific authentication.

Supported Providers

AWS ECR

Amazon Elastic Container Registry with scoped credentials via STS AssumeRole.

Configuration:

[registry]
type = "ecr"
region = "us-east-1"
account_id = "123456789012"
repo_prefix = "rise/"
role_arn = "arn:aws:iam::123456789012:role/rise-backend"
push_role_arn = "arn:aws:iam::123456789012:role/rise-backend-ecr-push"
auto_remove = false  # Tag as orphaned instead of deleting

How it works:

  1. Backend assumes push_role_arn with inline session policy scoped to specific project
  2. Backend calls AWS GetAuthorizationToken API with scoped credentials
  3. Returns credentials valid for 12 hours, scoped to single project repository
  4. CLI uses credentials to push images

Image path: {account}.dkr.ecr.{region}.amazonaws.com/{repo_prefix}{project}:{tag}

Example: 123456789012.dkr.ecr.us-east-1.amazonaws.com/rise/my-app:latest

Docker Registry

Works with any Docker-compatible registry (Docker Hub, Harbor, Quay, local registries).

Configuration:

[registry]
type = "oci-client-auth"
registry_url = "localhost:5000"
namespace = "rise-apps"

How it works:

  1. Backend returns registry URL to CLI
  2. CLI uses existing Docker credentials from ~/.docker/config.json
  3. No credential generation - relies on pre-authentication via docker login

Common use cases:

  • Local development: docker-compose registry (port 5000)
  • Docker Hub: registry_url = "docker.io", namespace = "myorg"
  • Harbor: registry_url = "harbor.company.com", namespace = "project"

Local Development Registry

For local development, Rise includes a Docker registry in docker-compose:

registry:
  image: registry:2
  ports:
    - "5000:5000"
  volumes:
    - registry_data:/var/lib/registry

Start:

mise backend:deps  # Starts all services including registry

Access:

  • Registry API: http://localhost:5000
  • Registry UI: http://localhost:5001 (browse images)

Usage:

# List repositories
curl http://localhost:5000/v2/_catalog

# List tags
curl http://localhost:5000/v2/my-app/tags/list

# Deploy (automatically uses local registry)
rise deployment create my-app

⚠️ Production Warning: Local registry uses HTTP, has no auth, and uses Docker volumes. For production, use AWS ECR, GCR, or similar.

AWS ECR Production Setup

Architecture: Two-Role Pattern

Controller Role (rise-backend):

  • Create/delete ECR repositories
  • Tag repositories (managed, orphaned)
  • Configure repository settings
  • Assume the push role

Push Role (rise-backend-ecr-push):

  • Push/pull images to ECR (under rise/* prefix)
  • Used by backend to generate scoped credentials for CLI

Why two roles?

  • Separation: Controller manages infrastructure, push handles images
  • Least privilege: Scoped credentials limited to single repository
  • Temporary: 12-hour max lifetime, can’t delete repositories

Terraform Module

Use the provided modules/rise-aws module:

module "rise_ecr" {
  source = "../modules/rise-aws"

  name        = "rise-backend"
  repo_prefix = "rise/"
  auto_remove = false

  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

output "rise_ecr_config" {
  value = module.rise_ecr.rise_config
}

Apply:

cd terraform
terraform init
terraform apply
terraform output rise_ecr_config

With EKS + IRSA

Configure module for IRSA:

module "rise_ecr" {
  source = "../modules/rise-aws"

  name                   = "rise-backend"
  repo_prefix            = "rise/"
  irsa_oidc_provider_arn = module.eks.oidc_provider_arn
  irsa_namespace         = "rise-system"
  irsa_service_account   = "rise-backend"
}

Helm values:

serviceAccount:
  create: true
  iamRoleArn: "arn:aws:iam::123456789012:role/rise-backend"

config:
  registry:
    type: "ecr"
    region: "us-east-1"
    account_id: "123456789012"
    repo_prefix: "rise/"
    role_arn: "arn:aws:iam::123456789012:role/rise-backend"
    push_role_arn: "arn:aws:iam::123456789012:role/rise-backend-ecr-push"
    # NO static credentials with IRSA

With IAM User (Non-AWS)

For running Rise outside AWS:

module "rise_ecr" {
  source = "../modules/rise-aws"

  name            = "rise-backend"
  repo_prefix     = "rise/"
  create_iam_role = false
  create_iam_user = true
}

# Store credentials securely
resource "aws_secretsmanager_secret_version" "rise_ecr_creds" {
  secret_id = aws_secretsmanager_secret.rise_ecr_creds.id
  secret_string = jsonencode({
    access_key_id     = module.rise_ecr.access_key_id
    secret_access_key = module.rise_ecr.secret_access_key
  })
}

Configuration

Registry configuration is in config/ directory. See the registry examples at the top of this document for TOML configuration format.

Configuration file precedence (highest to lowest):

  1. local.yaml (not checked into git, for local overrides)
  2. {RISE_CONFIG_RUN_MODE}.yaml (e.g., production.yaml, development.yaml)
  3. default.yaml

Environment variable substitution: You can reference environment variables in config files using ${VAR_NAME} or ${VAR_NAME:-default} syntax.

API Endpoint

Request credentials for a project:

GET /registry/credentials?project=my-app
Authorization: Bearer <jwt-token>

Response:

{
  "credentials": {
    "registry_url": "123456.dkr.ecr.us-east-1.amazonaws.com",
    "username": "AWS",
    "password": "eyJwYXlsb2FkIjoiS...",
    "expires_in": 43200
  },
  "repository": "my-app"
}

Security

ECR Credential Scope:

  • Scoped to specific project using STS AssumeRole with inline session policies
  • Credentials for my-app can only push to {repo_prefix}my-app*
  • 12-hour lifespan (AWS enforced)

Docker:

  • No credential scoping
  • Use registry-specific access controls

Best Practices:

  1. Use IAM roles (ECR): Avoid static credentials
  2. Enable HTTPS: Always use TLS in production
  3. Monitor access: Track credential requests and usage
  4. Rotate credentials: For Docker registries, rotate regularly
  5. Least privilege: Scope credentials to minimum permissions

Troubleshooting

“Access Denied” when pushing (ECR):

  1. Verify controller role can assume push role
  2. Check push role permissions
  3. Ensure repository exists with correct prefix
  4. Verify STS session policy scope

“Connection refused” to registry (Docker):

docker-compose ps registry
docker-compose logs registry
docker-compose restart registry

Images not persisting (Docker):

docker volume ls | grep registry
docker-compose down -v  # Removes volumes!

Extending Registry Support

To add a new registry provider:

  1. Implement RegistryProvider trait in rise-backend/src/registry/providers/
  2. Add provider to RegistryConfig enum
  3. Register provider in create_registry_provider()

Potential future providers: JFrog Artifactory, GCR, ACR, GHCR, Quay.io