Skip to content

AWS Resources & Runtime Access

Applications use short-lived ECS task-role credentials — never IAM users or long-lived access keys. Declare resources once and bind only the services that need them.

yaml
resources:
  uploads:
    type: s3
    create:
      versioning: true
    retain: true

  events:
    type: sns
    existing:
      arn: arn:aws:sns:us-east-1:123456789012:application-events

  reporting:
    type: rds
    existing:
      identifier: reporting-prod
      endpoint: reporting.internal
      port: 5432
      security_group_id: sg-0123456789
      secret_arn: arn:aws:secretsmanager:us-east-1:123456789012:secret:reporting
      kms_key_arn: arn:aws:kms:us-east-1:123456789012:key/00000000-0000-0000-0000-000000000000

services:
  web:
    type: web
    port: 8080
    health_check: /health
    bindings:
      - resource: uploads
        access: [list, read, write]
        prefix: user-content
      - resource: events
        access: [publish]
      - resource: reporting
        access: [connect]

Keel generates a separate IAM task role and security group for every service. S3, SNS, and SQS bindings become resource-scoped IAM statements. RDS and cache bindings become ingress rules owned by Keel on the linked security group. Resource identifiers are injected as environment variables such as UPLOADS_BUCKET_NAME and EVENTS_TOPIC_ARN; linked secrets use ECS secret injection.

Supported capabilities

TypeOwnershipCapabilities
S3managed or existinglist, read, write, delete
SNSmanaged or existingpublish
SQSmanaged or existingsend, consume
RDS / cacheexistingconnect
IAM roleexistingassume

An IAM-role binding grants sts:AssumeRole to the service. The target role's trust policy must separately trust the generated <app>-<service>-ecs-task role, particularly for cross-account access.

For an AWS service without a curated resource type, use an advanced custom IAM statement on the service and keep actions/resources narrowly scoped:

yaml
services:
  worker:
    permissions:
      - actions: [ses:SendEmail]
        resources: [arn:aws:ses:us-east-1:123456789012:identity/example.com]

For an existing resource or secret encrypted by a customer-managed KMS key, set kms_key_arn. Keel adds the required data-key/decrypt actions to the task or execution role. A cross-account resource policy, role trust policy, or KMS key policy must still authorize the generated service task role on the owning side.

Private subnets automatically receive a free S3 gateway endpoint when an S3 resource is configured. Set vpc.private_aws_endpoints: true to provision interface endpoints for configured SNS, SQS, and STS access.

What retain means

retain (default true) marks a Keel-managed resource as data you keep when the app goes away. It behaves differently for the two kinds of resource, because AWS constrains them differently:

  • S3, SNS, SQSkeel destroy releases the resource from Keel's state and leaves it running in AWS. Keel no longer manages it, and AWS keeps billing you for it until you delete it yourself. The teardown prints the exact aws CLI command for each one. On the normal apply path retain also emits lifecycle { prevent_destroy = true }, which protects the resource from being replaced by an ordinary keel infra apply.

  • The databaseretain cannot leave an RDS instance running, because the database sits inside the VPC and holds a security group the teardown removes; AWS could not delete the VPC around it. What survives is a final snapshot. database.retain turns on deletion_protection and guarantees that snapshot, and the teardown prints the restore-db-* command for it.

keel destroy names everything it will delete and everything it will keep, then asks you to type the app name. Two flags change what survives:

  • --delete-retained destroys retain: true resources along with the stack.
  • --no-final-snapshot deletes the database without a snapshot.

Deployment history deliberately survives a destroy (the record that something was deployed outlives the environment); remove it with --prune-history or keel history prune later. See Interruptions & Rescue for what else outlives an environment and why.

After redeploying an app whose resources were retained

The new keel up will collide with the surviving names — delete them, rename the resource in keel.yml, or import them first.

Resource commands

bash
# Create and bind a managed resource
keel resources add s3 uploads --versioning --bind web:list,read,write

# Link an existing resource without transferring ownership to Keel
keel resources link arn:aws:sns:us-east-1:123456789012:application-events \
  --as events --bind web:publish

# Change access later
keel resources bind uploads worker --access read
keel resources unbind uploads worker

# Inspect the complete app inventory and effective rules
keel resources
keel resources show uploads
keel resources show @load-balancer
keel resources --service web
keel resources --json

# Inspect access from a service's point of view
keel access show web
keel access explain web uploads

Built-in resources use @ names in the inventory, including @vpc, @cluster, @registry, @load-balancer, @logs, and optional add-ons such as @database, @cache, @domain, and @waf.

Keel — the AWS CLI you've always wanted.