Skip to content

Configuration (keel.yml)

Everything Keel manages is described by one file at the root of your project. This page covers the anatomy; see the keel.yml Reference for every field, type, default, and validation rule.

A complete example

yaml
# keel.yml
name: my-api
region: us-east-1
mode: advanced            # basic | advanced | custom

services:
  web:
    type: web             # web | worker | scheduled
    port: 8080
    health_check: /health # or a block, see below
    command: ["bundle", "exec", "puma"]   # overrides the image CMD
    cpu: 256              # 256 | 512 | 1024 | 2048 | 4096
    memory: 512
    desired_count: 2
    autoscaling:
      min: 2
      max: 10
      target_cpu: 60

environment:              # merged into every service
  RAILS_ENV: production
secrets:                  # added to every service, read from SSM
  - SECRET_KEY_BASE

release:                  # runs once per deploy, before any service takes traffic
  command: bundle exec rails db:migrate

logs:
  retention_days: 365     # default 14; -1 never expires

database:
  engine: postgres        # postgres | mysql | aurora-postgres | aurora-mysql
  version: "16"
  instance: db.t4g.micro
  storage: 20
  multi_az: true
  retain: true            # deletion protection + final snapshot (default)
  backup_retention_days: 7
  name: appdb             # default keeldb
  username: appuser       # default keeluser

cache:
  engine: valkey          # valkey | redis
  node_type: cache.t4g.micro

domain:
  name: api.example.com
  zone: example.com

waf:
  enabled: true

pipeline:
  source: github          # github | codecommit
  repo: myorg/my-api      # required for github; optional for codecommit
  branch: main
  # connection_arn:       # override the org's CodeConnections connection

tags:                     # applied to every managed AWS resource
  team: platform
  cost-center: eng

settings:                 # project-level CLI behavior
  verbose: false
  cautious: false

Optional sections (database, cache, domain, waf, resources, environments) can be omitted entirely.

Services

Each entry under services: becomes an ECS Fargate service. Three types exist:

  • web — receives traffic from the load balancer. Requires port and a health check. Only one web service can share a load balancer today; path-based routing is not implemented, so a second one is rejected at validation rather than provisioned with a target group nothing routes to.
  • worker — no load balancer, no port; runs continuously.
  • scheduled — runs on a schedule instead of continuously.

command

command overrides the image's CMD, so one image can run a web process and a worker process. Without it every service runs the same default command — and a worker silently boots a second copy of the web server.

It is a list of arguments, not a shell line. The scalar form is split on whitespace, but shell syntax (&&, |, $, redirection) is rejected: the container command is PID 1, and a shell there does not forward SIGTERM, so every task would be hard-killed on every deploy. Use the list form when you need a shell: ["sh", "-c", "first && second"].

Health checks

health_check accepts a path, or a block when the defaults do not fit:

yaml
services:
  web:
    health_check:
      path: /up
      grace_period: 90      # seconds before ECS may replace a new task (default 60)
      interval: 30
      timeout: 5
      healthy_threshold: 3
      unhealthy_threshold: 3
      matcher: "200-299"
      deregistration_delay: 30

grace_period is the one worth knowing about

ECS defaults the health-check grace period to 0, which makes a task eligible for replacement the moment it registers — so an app that takes 20–40 seconds to boot can be killed before it ever serves a request, and then loops. Keel defaults it to 60 seconds, and every service gets a deployment circuit breaker, so a bad deploy rolls back instead of restarting forever.

Shared environment and secrets

Anything shared by every service belongs at the top level rather than repeated:

yaml
environment:            # merged into every service
  RAILS_ENV: production
  APP_HOST: app.example.com
secrets:                # added to every service
  - SECRET_KEY_BASE

services:
  web:
    environment:
      LOG_LEVEL: debug  # a service-level value wins over the app-level one

Precedence, lowest to highest: app base → app environment override → service base → service environment override.

Settings

Two project-level behaviors change how commands communicate, persisted under settings: and shared across every environment. Command-line flags override them for a single invocation.

  • verbose — narrate every action a command takes as it happens (→ Triggering CodeBuild — my-api-prod-web-build).
  • cautious — preview the bulleted list of actions a command will take and ask for confirmation before it changes any AWS state.
bash
keel settings                       # list current values
keel settings set cautious true
keel deploy --verbose               # override for one run

Where to go next

Keel — the AWS CLI you've always wanted.